typescript

Typescript Union is not Restrictive Enough


When I create a union between two object types (A and B), I expect a variable assigned to that union type can only take on the shape of either A or B.

Unexpectedly, bad does not have a type error here, even though intuitively it should because it does not satisfy the constraints of A nor B. Typescript Playground Link

type A = {a: string}
type B = {a: string, b: string, c: string}
type Foo = A | B

// this should be a type error, but it isn't?
// to be valid, it should either:
// - not have "c" property
// - add the "b" property
const bad: Foo = {
  a: "",
  c: ""
}

How can I improve Foo so that the bad variable will fail with a type error?


Solution

  • What you want is a discriminated union:

    type A = { kind: 'A'; a: string };
    type B = { kind: 'B'; a: string; b: string; c: string };
    type Foo = A | B;
    
    // type error
    const invalid: Foo = {
        kind: 'B', // discriminant
        a: '',
        c: '',
    };
    

    The discriminant enforces Foo to be exactly of either type A or B.