首先,您需要定义event
属性的字符串literal type和预期的callback
参数data
的类型之间的映射.幸运的是,TypeScrip有一种简单的方法来表示从字符串文字类型到任意类型的映射:它只是一个object type.如下interface
条所示:
interface EventMap {
a: A;
b: B;
c: C;
}
我们有一个,我们可以将func()
定义为generic函数,如下所示:
declare function func<T extends (keyof EventMap)[]>(arg: {
items: [...{ [I in keyof T]: {
event: T[I],
callback: (data: EventMap[T[I]]) => void
} }]
}): void;
这里,类型参数T
表示items
数组中event
个属性中的tuple个.因此,如果您按照示例中所示的方式调用func()
,那么我们希望T
等于["a", "b", "c"]
.
当您调用func()
时,编译器会查看参数的items
属性,并try 将其与类型[...{[I in keyof T]: {event: T[I], callback: (data: EventMap[T[I]]) => void }]
匹配.
这是一个mapped tuple type,它被包装在variadic tuple type [...
+]
中,以给编译器一个提示,您希望将T
推断为元组,而不是无序数组(请参见microsoft/TypeScript#39094,其中它说"类型[...T]
,其中T
是类似数组的类型参数,可以方便地用于指示推断元组类型的首选项").
由于{[I in keyof T]: {⋯T[I]⋯}}
是homomorphic映射类型(参见What does "homomorphic mapped type" mean?),编译器将能够从items
的元素推断T
.对于元组类型T
的数字索引I
处的每个元素T[I]
,items
的对应元素应该具有该类型T[I]
的event
属性以及其data
回调参数是EventMap[T[I]]
类型的callback
属性,这意味着具有键T[I]
的WE index into EventMap
.其目的是编译器将从event
属性推断T[I]
,然后使用它来contextually type callback
参数data
.
让我们来测试一下:
func({
items: [
{
event: 'a',
callback: (data) => {
// ^?(parameter) data: A
},
},
{
event: 'b',
callback: (data) => {
// ^?(parameter) data: B
},
},
{
event: 'c',
callback: (data) => {
// ^?(parameter) data: C
},
},
],
});
// function func<["a", "b", "c"]>(⋯): void;
看上go 不错.编译器根据需要为T
推断出["a", "b", "c"]
,并且每个callback
的data
参数被适当地上下文类型化.
Playground link to code个