Get Type of Union By Discriminant

前端 未结 1 1976
我在风中等你
我在风中等你 2021-01-06 19:14

Suppose there is a union type Thing grouping together types Foo, Bar and Baz with the discriminating property

1条回答
  •  既然无缘
    2021-01-06 19:40

    In TypeScript v2.7 and earlier, there is no programmatic way to do this. It is easier to have TypeScript build unions programmatically than it is to inspect them. Therefore, you could do this instead:

    interface UnionSchema {
      Foo: {foo: string},
      Bar: {bar: number},
      Baz: {baz: boolean}
    }
    
    type Union = {
      [P in K]: UnionSchema[K] & {tag: K}
    }[K]
    

    Now you can use Union as you did before, but the individual union constituents can be referred to as Union<'Foo'>, Union<'Bar'>, and Union<'Baz'>. For convenience you can still give them names:

    interface Foo extends Union<'Foo'> {}
    interface Bar extends Union<'Bar'> {}
    interface Baz extends Union<'Baz'> {}
    

    And type your function like this:

    type TypeToFunc = {
      readonly [T in U['tag']]: (x: Union) => string
    }
    const typeToFunc: TypeToFunc = {
      // x must be of type Foo
      Foo: x => `FOO: ${x.foo}`,
      // x must be of type Bar
      Bar: x => `BAR: ${x.bar}`,
      // x must be of type Baz
      Baz: x => `BAZ: ${x.baz}`,
    }
    

    Starting in TypeScript v2.8, there will be a feature called conditional types which allows a lot more expressivity in the type system. You can write a general union discriminator like this:

    type DiscriminateUnion = 
      T extends Record ? T : never
    

    And then, with your original definitions:

    interface Foo {
      tag: 'Foo'
      foo: string
    }
    
    interface Bar {
      tag: 'Bar'
      bar: number
    }
    
    interface Baz {
      tag: 'Baz'
      baz: boolean
    }
    
    type Union = Foo | Bar | Baz
    

    You get the almost magical:

    type TypeToFunc = {
      readonly [T in U['tag']]: (x: DiscriminateUnion) => string
    }
    

    which also works. You can try this out now if you install typescript@next from npm... otherwise you'll need to wait.


    Hope that helps; good luck!

    0 讨论(0)
提交回复
热议问题