DEV Community

Cover image for Do you really know TypeScript? (1): Thinking in sets
Alex Menor
Alex Menor

Posted on • Edited on

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 or user.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 🤼‍♀️

Seeing types as sets of values

// what can we assign to it?

const foo: Bar = ???
Enter fullscreen mode Exit fullscreen mode

We can assign any value that belongs to a subtype of Bar.

Image description

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.

Image description

Structural typing

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'
}
Enter fullscreen mode Exit fullscreen mode

That wouldn't be possible in nominal type systems.

Let's see a slightly harder example:

type File = {
    name: string
    extension: string
}

type Folder = {
    name: string
    color: string
}

type DesktopItem = File | Folder
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

The empty set and the universal set

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.

Things to remember

  • Types are sets of values
  • One value of type Y is assignable to a type X if Y is a subtype of X.
  • Type compatibility in TypeScript is determined by structural typing.

Resources to go deeper

Top comments (1)

Collapse
 
ajinkyax profile image
Ajinkya Borade

Its time Typescript should now get Type comprehension :P
{ n | n E Q, n mod 2 = 0}