Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does typescript show an error on this scenario with a union type?

Tags:

typescript

On this scenario:

type Type = {
    a: string;
    b: string;
} | {
    a: number;
    b: number;
};

const func = ({a, b}: Type): string => {
    if (typeof a === 'string') {
        return b;
    }

    return 'w/e';
}

func({a: 1, b: 'w/e'});

I get an error on return b; stating

Type 'string | number' is not assignable to type 'string'.

  Type 'number' is not assignable to type 'string'.

From what I understand by the error, Typescript interprets the Type type as

type Type = {
    a: string | number;
    b: string | number;
};

, but even if that is the case, why can't I call the func function with a string and a number?

The error on the call states

Argument of type '{ a: number; b: string; }' is not assignable to parameter of type 'Type'.

  Type '{ a: number; b: string; }' is not assignable to type '{ a: number; b: number; }'.

    Types of property 'b' are incompatible.

      Type 'string' is not assignable to type 'number'.

(ts version: 4.7.2)

Playground link

  • Edit after UnholySheep's comment:

The function call with the wrong types was just to test my hypothesis on why I get the previous error. Why do I get an error at return b; is what I don't understand.

like image 357
pkyriazo Avatar asked Jan 23 '26 21:01

pkyriazo


2 Answers

Ok, the answer is Discriminated Unions. I have to use a discriminant, a common property in each element of the union. That, discriminant, property:

  1. Has to be a primitive type, and
  2. Has to have a specific value and not that value's type. e.g. isPrimary: true, not isPrimary: boolean.

Then I can check for that property's value to assume the other properties' types.

Also, in Typescript versions < 4.6, If i have an object with a type specified as above, If i destructure it, then the new variables are considered independent and I will continue getting an error similar to the initial example. Destructuring works in version 4.6 or higher.

  • Playground example (version 4.6)

  • Playground example (version 4.5)

Helpful links:

  • @Austaras's link to Microsoft's post announcing destructuring support
  • Understanding Discriminated Unions
  • Typescript's Singleton types

Thank you @Austaras and @coglialoro

like image 66
pkyriazo Avatar answered Jan 25 '26 19:01

pkyriazo


TS cannot track the relationship of a and b after you destruct them, so they both be inferred string | number. TS 4.6 introduced a limited control flow analysis for destructing, which only applies to discriminated union.

like image 26
Austaras Avatar answered Jan 25 '26 19:01

Austaras