Home > Net >  Discriminated union discriminated on optional property
Discriminated union discriminated on optional property

Time:04-16

I've been trying but failing to get this union type to work as I want, is this just not supported?

type TestA = { href: string; a: string };
type TestB = { href?: never; b: string };

type TestUnion = TestA | TestB;

const test = (x: TestUnion) => {
  if (x.href) {
    console.log("X is TestA", x.a);
  } else {
    console.log("x should be TestB, but is TestUnion", x.b); // error
  }
};

CodePudding user response:

In

const testBad = (x: TestUnion) => {
    if (x.href) {
        console.log("X is TestA", x.a.toUpperCase());
    } else {
        console.log("x should be TestB, but is TestUnion", 
          x.b.toUpperCase());
          //~ <-- error, property b does not exist on TestA
    }
};

the type of x.href starts off as string | undefined. Using (x.href) as a condition in an if statement is performing a truthiness check. And while a truthy x.href implies that x.href can be narrowed to string, a falsy x.href does not imply that x.href can be narrowed to undefined, because the empty string "" is falsy.

Thus you can get into the else block with a TestA unintentionally like this:

testBad({ href: "", a: "" }); // compiles, but
// RUNTIME ERROR            
  • Related