TypeScript Advanced Typing Examples
This article will show you throught examples how far you can go with TypeScript typing and maybe help you to improve your code.
Structure of this article :
1/ The examples are sorted by difficulty (easy first).
2/ The examples are first, the explainations follows.
Examples
#1 Inheritance rewrite
#2 Deep inheritance rewrite
#3 Type mutations
#4 Optional nested key
Explainations
#1 Inheritance rewrite
Let’s start with an easy example usingextends
:
As you probably already know, using extends
will makes Bar
to contains every attributes defined in Foo
. Now what if we wants to redifine keyB
as a Date
instead of a number
?
We would do :
But it does not work :
The soluce is to extends
every key of Bar
except keyB
so it available to be declared as aDate
:
#2 Deep inheritance rewrite
In this example, we seek to customize the Library
interface, by changing the type of the attribute color
in Books.
And of course, we do not want to rewrite eveything !
As we saw in the first example, we use Omit<Library, 'books'>
to specify which attribute we are going to rewrite.
Then, when rewriting books, we apply the same logic. Omit<Library['books'][0], 'color'>
state that we wants to keep every attribute of books
except color
that we are going to rewrite.
The &
here act as a concatenation of both types :
- Everything in
books
exceptcolor
- The new
color
definition
#3 Type mutations
We are here creating a new type, removing the readonly
property of every first level variables. Example :
#4 Optional nested key
When you are dealing with MongoDB (NoSQL database), there a difference between the data you are pushing to the database and the data extracted from the database. MongoDB will automatically adds _id
keys to your data.
It would be a shame to rewrite the whole interface. Instead, we will have a TypeScript type that will makes the _id
keys to be optionals. One interface only.
Let’s break down PartialK
and NestedPartialK
:
PartialK
first is working on one level. It’s first argument T
is the level to be treated. The second argument K
contains the keys name’s to make optionals.
The expression :
1/ Extract<keyof T, K>
returns the list of the keys that are declared in the object and inK
. Example : If K
is _id
and the object doesnt contains the _id
key, the Extract
will returns nothing. Otherwise it would return _id
.
2/ Pick<T, Extract<keyof T, K>>
pick from the objects every keys that has matched in the Extract
.
3/ Partial<Pick<T, Extract<keyof T, K>>>
then we makes all theses keys optional.
In the following example, Example
is worth { _id?: undefined | string }
4/ Using the &
operator, we merge the object created before with a new object defined by Omit<T, K> extends infer O ? { [P in keyof O]: O[P] } : never
5/ Omit<T, K>
will takes every keys on the object except the one in K
which are the keys we want to turn optional.
6/ infer O ? { [P in keyof O]: O[P] } : never
is sugar. We use it to force TypeScript interpretation. The code without the extends infer ....
works :
But makes it terrible to use directly. TypeScript does not interpret it :
With extends infer ....
and TypeScript interpretation, we get :
So how does it work ? The extends
is a keyword that is forcing TypeScript to interpret the type. And we extends
what ? The result of the &
we calculated before. About the infer
it is used to be able to specify to Typescript the data to extends.
About NestedPartialK
: We loop on every key of the object
1/ In case the value behind the key is a Function
, keep the data.
2/ In case the value behind the key is an Array
, call NestedPartialK
again (recursivity).
3/ In case the value behind the key is an object
, call NestedPartialK
again (recursivity).
End
This is the end of this article, thank you for reading it! I hope that you learned something new about TypeScript typing today :)
Special thanks
Thanks to jcalz in stackoverflow about NestedPartialK
! https://stackoverflow.com/questions/59845907/partial-on-specific-key