假设有一个联合类型 Thing
将具有区分属性Foo
的{{1}},Bar
和Baz
类型组合在一起
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
如果没有,还有其他方法可以实现这种映射吗?
答案 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
,则可以立即尝试此操作...否则您需要等待。
希望有所帮助;祝你好运!