More advanced types with TypeScript generics

JavaScript TypeScript Uncategorized

Previously, we’ve discussed the basics of TypeScript Generics. This time, we take them to a higher level. In this article, we learn about index types. To do so, we also explore union types, the keyof keyword, and string literal types. Today we also learn mapped types and use them with conditional types.

Index types

Generics are very useful in many situations. By using index types, we can improve our types even further. To get to know them, let’s first learn what a union type and the keyof keyword are.

union type represents one of several types. To separate them, we use a vertical bar.

String literal types are often used with unions. A string literal can only be assigned a particular string value. It can be considered a subtype of a string type.

Now, let’s consider this simple interface:

By using the keyof keyword, we can achieve a union of string literal types.

All of the above knowledge gives us quite a bit of flexibility.

Introducing index types

TypeScript 2.1 introduced index types. They look the same as accessing a property of an object but refer to types.

We sometimes refer to index types as lookup types.

We can use the above in a dynamic manner. Let’s inspect this popular example:

When we use our  , the compiler checks if a string that we pass into it is an actual property of an object.

Argument of type ‘”property”‘ is not assignable to parameter of type ‘”id” | “name” | “email” | “role”‘.

When we return  , the TypeScript compiler performs a type lookup. Thanks to that, the return type of the   varies based on the passed string.

Property ‘toLowerCase’ does not exist on type ‘number’.

Creating a Map from an object

A real-life example of the above might be converting an object to a Map. TypeScript aside, the most straightforward way to do this is to use Object.entries.

The above code, even though valid, does not produce the most detailed types.

Property ‘toLowerCase’ does not exist on type ‘string | boolean’.

The error above indicates that the return type of the   function is a union type  . We know that the type of   is a string. Let’s fix that!

We can create our own interface that extends Map and provides more detailed typings.

Now, every time we use the   method, we get an exact type of property.

If you have some other solution to the above issue, feel free to share it

If you need, you can also provide types for the set function in a similar manner.

Mapped types

The mapped types allow us to create new types from existing ones. A common use case is to make all of the properties of an object read-only.

The above is such a common use-case that we now have a  type built-in and ready to use.

An example of its usage is the Object.freeze function. Let’s look into how TypeScript handles it:

As we can see, the Object.freeze function returns the object that is mapped using the  modifier.

TypeScript developers identified more useful modifiers that might come in handy, such as .

Even though we have a set of modifiers for different occasions, we might need to write some custom ones. When doing so, conditional types might be of some use.

Conditional type selects one of two types based on a condition

Summary

In this article, we’ve expanded more on the subject of generics in TypeScript. We’ve investigated the indexed types and mapped types. When doing so, we’ve also learned the keyof keyword, the union types. We’ve also stumbled upon string literal types and conditional types. Learning all of the above will definitely expand our TypeScript knowledge!

Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Andry
Andry
2 years ago

Awesome!