Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.

15 St Margarets, NY 10033
(+381) 11 123 4567



Using TypeScript const Assertions for Fun and Profit

How can we utilize the const assertion in TypeScript to flip the script and define types from immutable data?

Since TypeScript 3.4, we’ve been able to use the as const notation for creating “const assertions” on our literals. This is used to signal to the compiler that any object properties of the literal are read-only, any array literals are read-only, and lastly that any literal types used cannot be widened.

For example:

Means that the object foo has a single property bar such that its value is of type ”baz”. This is also an example of a killer feature of TypeScript known as literal types, whereby specific instances of a string or number can be used as a type.

If you look at the documentation for const assertions, they cite several examples of how this feature can be useful, including the ability to:

Defining Union Types From Immutable Data

All the above examples are great ways to utilize the const assertion in your code, but I think I’ve found another really nifty use for them: defining union types from immutable data. This can be used in order to make the usages of such data more type-safe, without the additional maintenance overhead!

Let me explain through a worked example…

Defining a Type-Safe Interface

Say I am developing an API that uses a well-defined set of cars, such that they only represent several manufacturers. I could start with something that looks like this:

All looks fine here at first glance, and such an interface should do the trick. However, since the type for manufacturer is string, and I know that the data only represents Audis and BMWs, then I’m leaving some type of safety on the table.

Since string is a type that is too general for the data, I may decide to leverage discriminated union types to restrict what values manufacturer can have, preventing me from creating a Car that isn’t either an Audi or a BMW:

Nice! Now my manufacturer field is type-safe, and it can only take the value ”AUDI” | “BMW”.

But what if my data requires changes, and a new manufacturer is added? For instance, if I have to add a Mercedes to the set of cars:

Well, the TypeScript compiler should complain here, which is a great start – however, now I have to change my Manufacturer type by hand to make sure it is up-to-date. Thus becoming yet another thing for me to maintain!

What if there was another way? Perhaps leveraging the compiler to create the Manufacturer type for me? 

Using the const Assertion to Create a Type-Safe Interface

Let’s make a small change to the way we define the CARS array, adding the as const notation and removing the Car interface:

Firstly, I can create a type to match only the cars that exist in my data. We can do so by using their index in the CARS array, and the typeof operator. This can be useful to ensure that I cannot create a car that does not exist in the data.

Furthermore, I can access individual properties of each car in the data in order to create further utility types from their values!

This is great! Now the Manufacturer type is maintained for me by the compiler. As I add and remove cars from the data over time, I no longer need to respectively update the types associated with them.

Now if I were to define a function that takes a parameter of type Manufacturer, or declare a literal of type Manufacturer, I cannot pass it anything other than ”AUDI” | “BMW” | ”MERCEDES”.

If I were to define the same array of cars as above, omitting the const declaration, then the compiler will not be able to rely on this object being immutable. As a result, the type Manufacturer would be widened to string, and we’d be back to square one!

Summary and Extra Reading

As TypeScript users, there are loads of cool ways we can leverage the compiler. Both my colleagues at Instil & myself advocate for tinkering around to see what interesting stuff you can uncover.

In this example, we were able to use the const assertion in order to “flip the script”; leveraging the compiler to define a set of utility types from the data, rather than having to do things the other way around! It proved useful as we can get the benefits of being able to use the data in a more type-safe way, without the additional maintenance.

If you’d like to learn more, then you should check out these crazy, powerful TypeScript 4.1 features, some TypeScript testing tips, and this talk introducing an entirely different meaning to “TDD”: Type Driven Development

Credit: Source link

Previous Next
Test Caption
Test Description goes like this