Search code examples
typescriptunion-types

Typescript: convert a tagged union into an union type


I've the following tagged union interface

interface Example {
  a: TypeA;
  b: TypeB;
}

as an output I would like to convert this tagged union into an union type such as:

type oneOf<T> = ...

Where

var example: oneOf(Example); // would be { a: TypeA } | { b : TypeB }

I have not been able to map the type keys into an object having only the key as parameter. Any idea ?


Solution

  • You can do it with a combination of:

    interface Example {
      a: string;
      b: number;
    }
    
    type SingleProperty<T, K extends keyof T> = K extends any ? {[Prop in K]: T[Prop]} : never
    type UnionOfProperties<T> = { [K in keyof T]: SingleProperty<T, K> }[keyof T];
    type ExamplePropertiesUnion = UnionOfProperties<Example>
    

    This returns expected:

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

    While the above is correct, TS will allow the following

    var t: ExamplePropertiesUnion = {a: "", b: 42}
    
    

    Which is NOT what we usually want:

    Here below is the variant for a stricter type checking

    type FixTsUnion<T, K extends keyof T> = {[Prop in keyof T]?: Prop extends K ? T[Prop]: never}
    type oneOf<T> = { [K in keyof T]: Pick<T, K> & FixTsUnion<T, K>}[keyof T];
    // ok
    var z1: oneOf<Example> = { a: "" };
    // ok
    var z2: oneOf<Example> = { b: 5 };
    // error
    var z3: oneOf<Example> = { a: "", b: 34 };
    // error
    var z4: oneOf<Example> = { };
    

    Try it here

    See questions: