Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can TypeScript infer type of a discriminated union via "extracted" boolean logic?

I have been using discriminated unions (DU) more frequently and have come to love them. I do however have one issue I can't seem to get past. If you inline a boolean check for the DU, you can rely on TypeScript (TS) to automatically infer the type for you. However, if you extract the boolean check, TS can no longer narrow to the specific subtype of the DU. I’m aware of type guards, but I’d like to know why the compiler doesn’t support extracted online checks, specifically.

Is this a known limitation? Should I file a bug/feature request?

Example here (w/ TypeScript Playground Link):

type A = { type: "A"; foo: number };
type B = { type: "B"; bar: string };
type DU = A | B;

const checkIt = (it: DU) => {
  const extractedCheck = it.type === "A";

  if (extractedCheck) {
    // it does not get narrowed
    console.log(it.foo); // Error: Property 'foo' does not exist on type 'DU'.
  }

  if (it.type === "A") {
    // but this is fine
    console.log(it.foo);
  }
};

like image 502
Julian Avatar asked Sep 01 '25 10:09

Julian


2 Answers

UPDATE FOR TS4.4:

TypeScript 4.4 will introduce support for saving the results of type guards to a const, as implemented in microsoft/TypeScript#44730. At this point, your code example will just work:

const checkIt = (it: DU) => {
  const extractedCheck = it.type === "A";

  if (extractedCheck) {
    console.log(it.foo); // okay
  }

};

Playground link to code


ANSWER FOR TS4.3 AND BELOW:

There is an existing feature request at microsoft/TypeScript#12184 to allow such type guard results to be "saved" into a named value to be used later. The request is open but listed as "revisit" because there's no obvious way to implement it in a performant way. The word from the lead architect of the language is:

This would require us to track what effects a particular value for one variable implies for other variables, which would add a good deal of complexity (and associated performance penalty) to the control flow analyzer. Still, we'll keep it as a suggestion.

So I wouldn't expect to see such a feature in the language anytime soon, unfortunately.


My suggestion is just to continue using your inline type check. If you have a more complex type guard situation it may be worthwhile to make a user-defined type guard function but I don't see that as an improvement for the case in your example code.


Okay, hope that helps; good luck!

like image 96
jcalz Avatar answered Sep 03 '25 02:09

jcalz


Currently nope as mentioned by @jcalz, however there's a minimal workaround that you can always apply to overcome this problem.

The trick is to make extractCheck a function that returns type predicate.
(Refer https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)

The following is a working example:

type A = { type: "A"; foo: number };
type B = { type: "B"; bar: string };
type DU = A | B;

const checkIt = (it: DU) => {
  // Make `extractCheck` a function 
  const extractedCheck = (it: DU): it is A => it.type === "A";

  if (extractedCheck(it)) {
    console.log(it.foo);  // No error
  }
};
like image 37
Wong Jia Hau Avatar answered Sep 03 '25 03:09

Wong Jia Hau