Search code examples
typescriptunion-types

TypeScript add properties to specific union cases


I have this type (simplified):

type T1 = {
  kind: 'a';
  prop: string;
} | {
  kind: 'b';
  prop2: number;
};

Now I want to add a property prop3: boolean but only to the case of kind: 'a', like I written:

type T2 = {
  kind: 'a';
  prop: string;
  prop3: boolean;
} | {
  kind: 'b';
  prop2: number;
};

I tried the following:

type T2 = T1 & { prop3: boolean }; // Not works, adds to both `kind: 'a'` and `kind: 'b'`

type T3 = T1 & { kind: 'a'; prop3: boolean } // Adds to `kind: 'a'` properly, but intersects `kind: 'b' & 'a'` for `kind: 'b'`, and thus `kind: never`, and thus the whole case is `never`, so it omits `kind: 'b'` entirely - equals to `{ kind: 'a', prop: string, prop2: string }`

I suspect the answer has something to do with the built-in utility types like Exclude and Omit, but can't find a proper solution.

Any help is appreciated. Thanks in advance.


Solution

  • I think if you're going to do this sort of thing often and need a utility type for it, you can create your own:

    type IntersectExtract<T, U, V> = T extends U ? T & V : T
    

    This means: intersect V with just those members of the union T that are assignable to U:

    type T2 = IntersectExtract<T1, { kind: 'a' }, { prop3: boolean; }>
    
    /* type T2 = {
        kind: 'b';
        prop2: number;
    } | ({
        kind: 'a';
        prop: string;
    } & {
        prop3: boolean;
    }) */
    
    declare const t2: T2;
    if (t2.kind === "a") {
      t2.prop3 === true // okay
    } else {
      t2.prop3 === true // error
      // ~~~~~ <-- prop3 does not exist on type {kind: "b", prop2: number}
    }
    

    Looks like what you want.

    Playground link to code