通过判别式获取联合类型
Get Type of Union By Discriminant
假设有一个 联合类型 Thing
将类型 Foo
、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
的标签并使用映射到标签的类型中的相应接口。问题是:是否可以通过标记值从联合类型中检索类型?
interface Tagged {
tag: string
}
type TypeToFunc<U extends Tagged> = {
// Is it possilbe to retrieve the type for the given tag from the union type?
// What to put in place of the ???
readonly [T in U['tag']]: (x: ???) => 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.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
}
这也有效。如果您从 npm
安装 typescript@next
,现在就可以试试这个了……否则您需要等待。
希望对您有所帮助;祝你好运!
假设有一个 联合类型 Thing
将类型 Foo
、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
的标签并使用映射到标签的类型中的相应接口。问题是:是否可以通过标记值从联合类型中检索类型?
interface Tagged {
tag: string
}
type TypeToFunc<U extends Tagged> = {
// Is it possilbe to retrieve the type for the given tag from the union type?
// What to put in place of the ???
readonly [T in U['tag']]: (x: ???) => 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.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
}
这也有效。如果您从 npm
安装 typescript@next
,现在就可以试试这个了……否则您需要等待。
希望对您有所帮助;祝你好运!