DEV Community

Cover image for Do you really know TypeScript? (4): Type assertions
Alex Menor
Alex Menor

Posted on • Updated on

Do you really know TypeScript? (4): Type assertions

In this post (the last of the series 😒) we are going to understand type assertions and compare them against type declarations.

What do you use type assertions for?

There are situations where you know more than TypeScript can infer.

let item: Item = {...}

type Item = {
  name: string
}

type FileItem =  Item & {
  extension: string
}

// We know for sure that item
// is also a file
printFile(item as File)

Enter fullscreen mode Exit fullscreen mode

Golden rule for using assertions

You can only assert from one type to another if either type is a subset of the other. 🧐

type Car = {
  numOfDoors: number
}

type Airplane = {
  numOfEngines: number
}

const car: Car = {numOfDoors: 5}

// Conversion of type 'Car' to type 'Airplane' may be a mistake
// because neither type sufficiently overlaps with the other. 
const airplane = car as Airplane
Enter fullscreen mode Exit fullscreen mode

An exception to this rule is when using unknown or any.
You can use these to bypass it:

  • unknown because is the universal set
  • any because disables the type checking
const airplane = car as unknown as Airplane
Enter fullscreen mode Exit fullscreen mode

Prefer type declarations to type assertions

This is a mistake that I've seen a lot!

type Car = {
  numOfDoors: number
  numOfAirbags: number
}

// Error: Property 'numOfAirbags' is missing
const car: Car = {numOfDoors: 5}

// No error
const car = {numOfDoors: 5} as Car
Enter fullscreen mode Exit fullscreen mode

When you use type assertions you are telling TypeScript to get out of the way, with type declarations you are making your intentions clear so it can help you.

Is as const a type assertion?

It is not.
Despite having a similar syntax, as const is used to hint the type system about values being immutable.

It is very situational, but could be useful for using the values of an array as literals, for example:

const coolBands = ['Oasis', 'AC/DC', 'Foo Fighters'] as const

// type CoolBands = "Oasis" | "AC/DC" | "Foo Fighters"
type CoolBands = typeof coolBands[number]
Enter fullscreen mode Exit fullscreen mode

Or for using the values of an object:

const coolBandsAndSingers = {
  'Oasis': 'Liam Gallagher',
  'AC/DC': 'Brian Johnson',
  'Foo Fighters': 'Dave Grohl'
} as const

// type CoolBands = "Oasis" | "AC/DC" | "Foo Fighters"
type CoolBands = keyof typeof coolBandsAndSingers

// type CoolSingers = "Liam Gallagher" | "Brian Johnson" | "Dave Grohl"
type CoolSingers = typeof coolBandsAndSingers[CoolBands]
Enter fullscreen mode Exit fullscreen mode

As it is the last post of this series, I also want to go through some topics that couldn't get a post for their own.

Don't type everything!

I did it, and probably so did you.

It is not bad, but can make the code too verbose and therefore harder to read.

As a rule of thumb, you should type very well:

  • Function and method signatures (parameters and return types)
  • Variables and constants when using object literals, to take advantage of excess property checking.

In a TDD like spirit, you should know your input and output types before implementing a function/method, so typing it from the beginning makes it easier for you to implement it.

Typing return types usually avoids implementation errors, specially if your function has many "paths".

Don’t use uppercase variants of primitive types

Probably you noticed that String or Number exist and wonder if you should use them as types.

The answer is no. Just stick to lowercase types for primitives string, number, boolean, etc.

These uppercase variants exist primarily for convenience, for example:

// charAt is not a property of
// the string primitive
"hey".charAt(1)

Enter fullscreen mode Exit fullscreen mode

JavaScript wraps the string primitive in String under the hood and uses the charAt method of String and then throws that object away.

// These wrappers don't have behave 
// as primitives

new String('hey') === new String('hey')

'hey' === new String('hey')
Enter fullscreen mode Exit fullscreen mode

It's been a pleasure to write this series and I wish you a very productive experience with TypeScript πŸ™‚

Thanks for reading!

Resources to go deeper

Discussion (0)