例如我写了下面的代码。
func({
items: [
{
event: 'a',
callback: (data) => {
},
},
{
event: 'b',
callback: (data) => {
},
},
{
event: 'c',
callback: (data) => {
},
},
],
});
我想要的是这个。
可以吗?
addEventListener
似乎已经工作得很糟糕,所以我尝试参考这部分,但没有成功。
首先,您需要定义 event
属性的字符串
literal type与预期
callback
参数 data
的类型之间的映射。幸运的是,TypeScript 有一种简单的方法来表示从字符串文字类型到任意类型的映射:它只是一个对象类型。就像下面的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
表示event
数组中items
属性的元组。因此,如果您按照示例中所示的方式调用
func()
,那么我们希望 T
为 ["a", "b", "c"]
。
当您调用
func()
时,编译器会查看参数的 items
属性,并尝试将其与类型 [...{[I in keyof T]: {event: T[I], callback: (data: EventMap[T[I]]) => void }]
进行匹配。
[...
+]
中,以向编译器提示您希望将T
推断为元组而不是无序数组(请参阅 microsoft/TypeScript#39094,其中显示“类型 [...T]
,其中 T
是类似数组的类型参数,可以方便地用于指示对元组类型推断的偏好”)。
并且因为
{[I in keyof T]: {⋯T[I]⋯}}
是 同态 映射类型(请参阅“同态映射类型”是什么意思?),编译器将能够从 T
的元素推断出 items
。对于元组类型 T[I]
的数字索引 I
处的每个元素 T
,items
的对应元素应该具有该类型 event
的 T[I]
属性,以及其 callback
的
data
属性回调参数的类型为
EventMap[T[I]]
,这意味着我们使用键 EventMap
索引到
T[I]
。目的是编译器将从 T[I]
属性推断 event
,然后使用它来根据上下文键入 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;
看起来不错。编译器根据需要推断出
["a", "b", "c"]
的 T
,并且每个 data
的 callback
参数都根据上下文适当地键入。