TypeScript: Type Predicate

When using Union, Type Predicate help you solve `Property X doesn't exist on type [...]`

ยท

2 min read

Today, I have come across a concept new to me in TypeScript: Type Predicates.

Let's see what problem Type Predicates solve and when to use them.

For more information, you can check the official documentation here

The problem: unknown type at compile time ๐Ÿ’ฅ

Let's say you need to fill your car. Your car is either running on diesel or unleaded.

type Diesel = {
    isDiesel: true,
}

type Unleaded = {
    isUnleaded: true,
}

type Fuel = Unleaded | Diesel

function fillMyCar(fuel: Fuel){
    if (fuel.isDiesel){
        console.log("Filled with Diesel!")
    } else if (fuel.isUnleaded){
        console.log("Filled with Unleaded")
    }
}

/* ๐Ÿ”ด Property 'isDiesel' does not exist on type 'Diesel | Unleaded'.
  Property 'isDiesel' does not exist on type 'Unleaded'. */

/* ๐Ÿ”ด Property 'isUnleaded' does not exist on type 'Diesel | Unleaded'.
  Property 'isUnleaded' does not exist on type 'Diesel'. */

Why do we encounter this error? When using Union in TypeScript, the compiler cannot know for sure the type of the object you are using until runtime.

If your car is unleaded, then the isDiesel property doesn't exist on it!

Type Predicates is a solution to this common problem.

Solution: Type Predicates ๐ŸŽฏ

A Type Predicate allows us to check and narrow the type of the argument at runtime

function isDiesel(fuel:Fuel):fuel is Diesel{
    return true;
}

The Type Predicate resides in fuel is Diesel.

It translates to: if fuel is Diesel, then execute the code. Else, return false.

Great. Now we can refactor our previous code:

type Diesel = {
    isDiesel: true,
}

type Unleaded = {
    isUnleaded: true,
}


type Fuel = Unleaded | Diesel

function isDiesel(fuel:Fuel):fuel is Diesel{
    return true;
}

function fillMyCar(fuel: Fuel){
    if (isDiesel(fuel)){
        console.log("Filled with Diesel!")
    } else if (fuel.isUnleaded){
        console.log("Filled with Unleaded")
    }
}

โ˜๏ธ Note that Typescript is smart enough to realise that if it's not Diesel, then it must be Unleaded since we only have two types in the union!

That's pretty cool if you ask me.

Conclusion โœ๏ธ

The goal of Type Predicates is to inform the compiler that we are checking the type at runtime and handling each case.

I am actually glad I came across this as I had encountered this issue multiple times in the past.

Today I learnt... Type Predicates ๐ŸŒ 

ย