Do discriminated unions only work with literal types?

Viewed 57

In the snippet below, I'm not able to discriminate the union type using the typeof operator.

function f(arg: { status: number; one: boolean } | { status: string; two: boolean }) {
    if (typeof arg.status === "number") {
        return arg // arg is still of the union type and is not narrowed
    } else {
        return arg // similar here
    }
}

But if status is changed to some literal type, then the union can be discriminated.

function f(arg: { status: "one"; one: boolean } | { status: "two"; two: boolean }) {
    if (arg.status=== "one") {
        return arg // here arg is of type { status: "one"; one: boolean }
    } else {
        return arg // and here it's of type { status: "two"; two: boolean }
    }
}

So, I wanted to know why does it not work in the first case, is it that discriminated unions only work with literal types or is there something else?

I tried to find in the docs to see if it's mentioned somewhere that discriminated unions only work with literal types but I couldn't find anything.

Link to playground

1 Answers

Well, this is kind of a limitation that Typescript currently has. It's been discussed a few times in the Typescript Github repo so I'll use some of the responses from there.

First, you should know that:

Type guards do not propagate type narrowings to parent objects. The narrowing is only applied upon access of the narrowed property which is why the destructing function works, but the reference function does not. Narrowing the parent would involve synthesizing new types which would be expensive.

So basically if you used the code below, you'd see that TS indeed is able to narrow down the type of that specific property:

function f1(arg: { status: number; one: boolean } | { status: string; two: boolean }) {
    if (typeof arg.status === "number") {
        const st = arg.status // st: number
    }
}

Narrowing the parent object is only done in specific scenarios, when the property is considered a discriminant for the union. A property is considered as a discriminant property if:

1- The property is a literal type as outlined here Discriminated union types.

2- The a property of a union type to be a discriminant property if it has a union type containing at least one unit type and no instantiable types as outlined here Allow non-unit types in union discriminants.

For more info, you could see here and here.

Related