19
Do you really know TypeScript? (1): Thinking in sets
I know many people, including myself, whose first experience with TypeScript was to write annotations in some variables and to add as any
until the transpiler stopped complaining.
But at some point you realize that you really should dig a little deeper into TypeScript and finally understand those errors.
It's true that you can technically build the same websites/systems with and without TypeScript, but its benefits are huge:
- Catch errors at compile time instead of at runtime
- Autocomplete ("it was
user.phone
oruser.phoneNumber
?" 🤔) - Language services (enabling some IDE's refactors for example)
- Better domain modelling
Using TypeScript is not only investing in the code's maintainability, it is also investing in the developer's productivity.
Okay, you are (or were already) convinced that TypeScript is worth learning, what is this series for?
In this series I will go through many aspects of the language that were not obvious to me even after having already done some projects with it.
You will stop wrestling with TypeScript 🤼‍♀️
// what can we assign to it?
const foo: Bar = ???
We can assign any value that belongs to a subtype of Bar
.
One cool thing of this system is that you can use set operators (among others) to create new types.
For example the union operator: number | string
is the set of all numbers and strings.
To know if a value belongs or not to a type, TypeScript only focuses on the shape that it has.
class Person {
name: string
}
// We didn't use the "new" keyword
const person: Person = {
name: 'Jame'
}
That wouldn't be possible in nominal type systems.
Let's see a little harder example:
type File = {
name: string
extension: string
}
type Folder = {
name: string
color: string
}
type DesktopItem = File | Folder
DesktopItem
contains all the objects that either have the properties (name and type) of File
or Folder
.
const item: DesktopItem = couldBeFileOrFolder
// should work, right?
givenItem.extension
It doesn't work because only File
has that property and the specific type of item
could be Folder
.
When we declare the union of two types the result is a type that has the intersection of the properties. In this case objects with a property name
of type string
, because it is the only property that they have in common.
It's also true the other way around: The intersection of two types results in a type that has the union of the properties.
keyof (A&B) = (keyof A) | (keyof B)
keyof (A|B) = (keyof A) & (keyof B)
If we can think of types as sets of values, what types are the empty set and the universal set?
never
is the empty set and unknown
is the universal set.
These are both very special and we'll talk about them soon.
- Types are sets of values
- One value of type
Y
is assignable to a typeX
ifY
is a subtype ofX
. - Type compatibility in TypeScript is determined by structural typing.
19