Understanding Angular/Typescript types

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:

  1. The Angular team uses the Fn suffix as a convention for all functions
  2. 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, an Observable of a boolean, a Promise of a boolean
  • a UrlTree, an Observable of a UrlTree, a Promise of a UrlTree.

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.

Typescript enums and why I avoid them

As an Angular coach, I review a lot of Angular applications. One thing that I see in a lot of code bases is the use of Typescript enums to store a bunch of constants:

The above code creates a new type CompassDirection with four possible values: CompassDirection.North, CompassDirection.East, etc.

Each constant gets assigned a numerical value starting at 0, so in that example, CompassDirection.North is equal to 0, and CompassDirection.North is equal to 3.

These values can be customized, and we can use strings, objects, or anything we want instead of numbers:

And that’s about all you can do with enums in Typescript.

Why I avoid enums

I avoid enums because they’re neither convenient nor performant. For instance, this is what the last example gets compiled into by the Typescript compiler:

That’s not pretty, and it comes at the extra cost of all that code being downloaded in the browser and then interpreted, which impacts performance.

Also, enums aren’t very convenient to be used in component templates. If I want to use an enum in a component template, I need this additional code to make that type accessible on the component instance (the this reference):

Instead, we could use a union type:

We still get a type associated with specific values, but now this gets compiled into the following:

No, I didn’t forget anything in that black rectangle: Union types are like interfaces and don’t get compiled into anything, meaning they do not increase the size of your code base or impact performance. Also, since those types are just the union of other types (such as strings or numbers), these constants can be used as-is in a component template without needing to tweak our component class.

In other words, union types preserve type safety without degrading our app’s performance, which is a win-win.

Typescript Cheatsheet: Type

A few months back, I shared a cheat sheet for Typescript Control Flow. Today, I want to share a cheat sheet on types, which are the main reason why Typescript is so useful.

A few interesting features that aren’t too well known:

  • The keyof operator and the Type[Property] illustrated in the Mapped Types section
  • One of my favorites: Union types and the even more powerful Template Union Types (bottom right corner of the cheat sheet)

You can click here to see the full-size cheatsheet.

Make types from JSON samples

First, a quick announcement: I’m running a donation-based online Angular Signals Workshop on December 14th and January 18th. If you’re interested, feel free to show your interest here, and remember that you don’t have to pay anything if you don’t want to or can’t.

Earlier this year, I shared how to generate type definitions for Typescript using json2ts, but that website has disappeared. A suggested superior alternative is Quicktype, which can also create DTOs and has several config options.

Recently, I heard about another option called MakeTypes. It’s as simple as the two previous tools, and it can also be downloaded via npm to be part of your local development process if that’s what you want:

Again, all you need to do is copy-paste your sample JSON and get instant Typescript interfaces (or proxy classes) to use in your project. And if you don’t want to use a website to create your types, head to the NPM page to download the tool.

Typescript Cheatsheet: Control Flow

A few weeks back, I shared a cheat sheet for Typescript classes. Today, I want to share a cheat sheet on control flow, which will change in Angular templates very soon.

Note the different type narrowing options, such as: if (“property” in object), which can come in handy instead of creating extra types. Type guards are exciting as well, though rarely used in Angular applications:

Click this link to see a larger version in a browser.

Typescript Cheatsheet: Classes

A few weeks back, I shared a cheat sheet for Typescript interfaces. Today, I want to share a cheat sheet on classes, which can be complemented with my past entry on Typescript visibility modifiers, as their behavior in Angular has specificities related to HTML templates.

Note the common syntax section with the different options for field declarations:

Click this link to see a larger version in a browser.

Best practices for using visibility modifiers

Yesterday, we talked about Typescript visibility modifiers in the context of Angular classes. Today, I want to focus on best practices and common scenarios that involve such visibility modifiers.

First, it’s very common to have Angular services that use RxJs Subjects. Subjects are somewhat dangerous because they can be used to emit data to all subscribers. As a result, it makes sense to “hide” a subject by making it private and then expose an Observable out of it as follows:

Note that we don’t need to make data$ public, because any field that isn’t private or protected is public by default. Using readonly enforces that other components/services cannot assign a new value to data$. As a result, the above code is safe: The business logic that decides when and how to emit data is “hidden” in our service (where it belongs), and yet the rest of the application can be notified when the data changes without being able to break that process by accident.

Of course, the same idea applies to Signals (see my best practices for Signals)

Another option suggested in that post is to define a getter method that returns the read-only object:

This is equivalent to using readonly. The value can’t be changed because we define a getter and no setter.

Let’s complete our best practices list from yesterday:

  1. Make every member private by default
  2. If the member is needed in the template of a component, make it protected
  3. If the member is meant to be fully public, go with public
  4. If the member should be accessible but not modifiable, use readonly or a single getter with no setter

Typescript visibility modifiers

Angular is mostly about Typescript classes, and Typescript classes have modifiers that alter the visibility of class members: public, protected, private, and readonly. Here is what you need to know about them.

Everything is public by default

If you don’t use a modifier on a class member, it is public by default. Any other class can see and use that member and change its value:

private means that the member isn’t visible outside of the class (including a component’s HTML template)

private is a way to enforce that other classes cannot access a member of your class. Used in a component, this indicates that we do not want that property to be used in the component’s HTML template. In a service, this suggests that we don’t want other components/services to see that member:

protected is in-between public and private. It makes the member accessible in a component’s template without making it fully public.

In the following example, our component property date is invisible from other Angular code in our app, but our HTML template can use it:

Best practice – My recommendation

If you want to stick to simple rules that make sense and are the safest, here’s what you can do:

  1. Make every member private by default
  2. If the member is needed in the template of a component, make it protected
  3. If the member is meant to be fully public, go with public

Tomorrow, I’ll add a couple more suggestions by introducing the readonly modifier. Stay tuned!

Typescript Cheatsheet: Interfaces

TypeScript is a crucial part of writing Angular apps, and the language has numerous tips, tricks, and syntaxes that can make our life easier. Today, I want to share this cheatsheet from the official Typescript website:

That cheat sheet has many interesting features; even the “Common Syntax” section has quite a few things that most people are unaware of. Click this link to see a larger version in your browser.

Best practice: Using types with the HttpClient

Here is an example of code I see way too often when people submit their code to our Angular certification exam:

While I could almost live with the above code, the following example makes me angry (especially because I tell people NOT to do that in bold uppercase characters in my instructions):

What’s the problem with that code? It uses any, and any is dangerous. It isn’t good because it turns off type safety. It makes your code less safe; it removes type information, it makes it less maintainable. It’s excruciating when we know that the HttpClient is nice enough to let us specify the type of the data we’re going to get as a response:

Of course, this type of information does not guarantee that the server is going to return data of that shape, so we have to make sure the server is indeed returning that exact type of data.

But the thing is, once you add that type to your HTTP request, you can (should I say, you must) do the following:

And this is important because when you subscribe to that Observable, Typescript knows what kind of data you receive:

And that is why we use Typescript. Using the above syntax enables autocomplete in your IDE; it brings type safety so you can’t make typos. There are only benefits to using it.