Avoiding Any in TypeScript – Advanced Types and Their Usage
Let’s see the definition:
Listing 1 Record definition from lib.es5.ts
And the usage:
Listing 2 Example of using Record type
As you can see, it means that the developer can enter any key, but the value has to be of a specific type. What’s more, we can combine it with other types, therefore making dictionaries with some pre-defined keys (of even different value types!) visible by IntelliSense, e.g.:
Listing 3 More advanced example of using Record type
I don’t think I need to write about the practical usage of this. Of course, we have a built-in ES6 collection called “Map” that offers a much better experience for dictionaries, but according to benchmarks, when we only want to set and get values, it’s faster to use objects (Record).
Mass modifiers of other type’s properties
What to do, when we already have a type with defined properties, but we don’t like how it is defined? Maybe we want all properties to be optional, required, or read only? TypeScript also has it covered with the types Partial, Required and Readonly. Let’s see their definitions:
Listing 4 Partial, Required and Readonly definitions from lib.es5.ts
Of course, as the article’s title tells us, only Partial can really be used in place of any, but all three are in a way linked to each other, so it would be a waste to not to write about them together.
They offer some quite practical usages. Readonly is very common in typings of React – in components there are some things that you can’t just edit, but you are the person who defines types like a component’s state or properties.
Partial is used wherever you want to offer the possibility to not provide the whole object. A popular usage example is providing the possibility to set the values of an object through one function (or constructor), rather than by setting properties separately. The following listing gives an example of this kind of usage.
Listing 5 Example of using Partial
Required is the opposite of Partial. However, here the usages are not so straightforward, since it’s checking if everything is set just at the types level during compilation. Since I’ve never seen any good, practical usages, I won’t provide any. If you have some, please let me know.
Picking and omitting specific a part of a type
Earlier we were discussing Partial, which allowed us to make everything in a type optional. That said, we may also want to take a specific part of an object without changing it to be optional (which makes typing even more strict). Let me introduce Pick:
Listing 6 Pick definition from lib.es5.ts
Pick works like this: it takes only those specific properties that are in the second type. Here’s an example of how it works:
Listing 7 Usage of Pick
Pick may not seem very useful, since in fact it only takes the given keys or keys from the second type. So you might ask: why don’t we just use this second type or build a type only from those keys? The reason is this – we keep information about the original type. And why not Partial? The difference is when you turn on strictNullChecks flag in compiler settings. Let’s review an example:
Listing 8 The difference between Pick and Partial
As you can see, Pick doesn’t allow setting a null value (also undefined) as was defined in the original type. Partial just makes everything nullable, so it does interrupt the original typing. In practice it’s used, for example, in React, in the setState function.
That said, I’ve mentioned omitting. We don’t have a built-in type for that, but we can build one for ourselves like this:
Listing 9 Definition of Omit type
In this way, we can perform a following thing:
Listing 10 Example of using Omit
If you compare objects e and f from this listing with c and d from listing 7, you can see that we get the complete opposite thing. An example of using Omit can be seen in Ramda to describe the result of a function called omit (quite a surprise, huh?).
Unknown and advanced tuple types
Both TypeScript features which I describe briefly in this part of the article were introduced in TypeScript 3.0, and I’ve already covered them in my article about TypeScript 3.0.
unknown is what should be used when we don’t know a proper type of object. Unlike any, it doesn’t let you do any operations on a value until we know its type (through proper type checks).
Through advanced tuple types I mean mostly extracting and spreading function arguments with tuple types, which was introduced in TypeScript 3.0. Thanks to them, we can define specific types for specific values in an array. While at first glance it doesn’t sound very helpful, they can be useful in typing functional programming features like compose.
I encourage you to review the article I’ve linked above for more detailed descriptions, along with usage examples, so you can have a better grasp of them.
This post was also published on ITNEXT.
Do you need more technical insights? Check out my other articles!