First, some news: I’m running a public 5 half-day online Angular class during the week of April 22nd. It’s the perfect class if you’re new to Angular or have some experience and want to ensure you know about all the fundamentals. It’s the ideal class to prepare for the Angular Level 1 certification exam.
On a side note, I can give private talks for your company or dev team on any topic, including Signals, the future of Angular, and more. Just email me for more info if you’d like to get such a talk planned in the future.
Today, I want to cover a tricky topic for many developers I interact with: Reading and understanding Typescript type definitions from the Angular framework.
For instance, let’s look at the type definition of the canActivate
function:
We can tell CanActivateFn
is a function because:
- The Angular team uses the
Fn
suffix as a convention for all functions - The type signature is
( params ) => returnType
, which is how TypeScript defines types for functions. The arrow symbol=>
is key there.
So, in that case, the function has two parameters, route
of type ActivatedRouteSnapshot
, and state
of type RouterStateSnapshot
. The function returns a type MaybeAsync<GuardResult>
.
Here is what the type definition of MaybeAsync
looks like:
What does that mean? MaybeAsync<T>
is a generic type, which means it works with any number of types, referred to as T
here. You can see T
as a type variable that gets replaced with an actual value decided when we use that type. For instance, if I end up using MaybeAsync
on a string
, T
becomes string
, and our type definition means:
type MaybeAsync<string> = string | Observable<string> | Promise<string>
So MaybeAsync<string>
can be either a string
, an Observable
that will return a string
, or a Promise
that will return a string
. That’s because the | character defines a union type and can be seen as a logical OR. In other words:
MaybeAsync<string> IS A string OR Observable<string> OR Promise<string>
.
Now, in the case of our CanActivate
function, the return type is MaybeAsync<GuardResult>
. On angular.io, most types are clickable (a lot less on angular.dev for now). If I click on GuardResult
on this page, I get to the following documentation entry:
So a GuardResult
is either a boolean
or a UrlTree
. This tells us that a CanActivate
function can return six possible different types:
- a
boolean
, anObservable
of aboolean
, aPromise
of aboolean
- a
UrlTree
, anObservable
of aUrlTree
, aPromise
of aUrlTree
.
In the past, the documentation would list the six types inline as follows, which is the same thing but a little harder to read. Additional types have been added in Angular 16 to improve the readability of such framework APIs:
Another tricky thing with types is that several features of the Angular framework support different options, resulting in multiple types of signatures. You can look at the toSignal function for such an example – there are 5 different overloads in that function signature, the most basic one being:
As an exercise, I invite you to examine the 5 overloads and try to understand where they come from and why they make sense.
If you encounter any other tricky type you can’t decipher, please send it my way, and I’ll be happy to cover it in a future newsletter entry.