通过判别获得联盟类型

时间:2018-02-12 16:06:19

标签: typescript type-inference typescript-typings union-types

假设有一个联合类型 Thing将具有区分属性Foo的{​​{1}},BarBaz类型组合在一起

tag

现在我想创建一个映射类型,我将遍历interface Foo { tag: 'Foo' foo: string } interface Bar { tag: 'Bar' bar: number } interface Baz { tag: 'Baz' baz: boolean } type Union = Foo | Bar | Baz 的标记并使用映射到标记的类型中的相应接口。问题是:是否可以通过标记值从联合类型中检索类型?

Union

如果没有,还有其他方法可以实现这种映射吗?

1 个答案:

答案 0 :(得分:9)

在TypeScript v2.7及更早版本中,没有以编程方式执行此操作。以编程方式使用TypeScript构建联合比检查它们更容易。因此,您可以这样做:

interface UnionSchema {
  Foo: {foo: string},
  Bar: {bar: number},
  Baz: {baz: boolean}
}

type Union<K extends keyof UnionSchema = keyof UnionSchema> = {
  [P in K]: UnionSchema[K] & {tag: K}
}[K]

现在您可以像以前一样使用Union,但可以将各个联合成分称为Union<'Foo'>Union<'Bar'>Union<'Baz'>。为方便起见,您仍然可以给他们起名字:

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

然后输入您的函数:

type TypeToFunc<U extends Union> = {
  readonly [T in U['tag']]: (x: Union<T>) => string
}
const typeToFunc: TypeToFunc<Union> = {
  // 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}`,
}

从TypeScript v2.8开始,将会有一个名为conditional types的功能,它可以在类型系统中提供更多的表现力。你可以写一个像这样的通用联合鉴别器:

type DiscriminateUnion<T, K extends keyof T, V extends T[K]> = 
  T extends Record<K, V> ? T : never

然后,根据您的原始定义:

interface Foo {
  tag: 'Foo'
  foo: string
}

interface Bar {
  tag: 'Bar'
  bar: number
}

interface Baz {
  tag: 'Baz'
  baz: boolean
}

type Union = Foo | Bar | Baz

你得到了几乎神奇的东西:

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

也有效。如果您从typescript@next安装npm,则可以立即尝试此操作...否则您需要等待。

希望有所帮助;祝你好运!