我正在尝试在 TypeScript 中创建一个通用函数,该函数接受一个对象,其中每个属性都是一个由特定联合类型 ('foo' | 'bar') 的字符串和回调函数组成的元组。回调函数的参数应输入为元组中的字符串。
这是我的代码:
type TInput = 'foo' | 'bar';
const func = <T extends object>(arg: {
[N in keyof T]: {
[K in TInput]: [K, (arg: K) => void]
}[TInput]
}) => {}
func({
flash: ['bar', (ctx) => {}], // Parameter 'ctx' implicitly has an 'any' type.ts(7006)
antman: ['foo', (ctx) => {}], // Parameter 'ctx' implicitly has an 'any' type.ts(7006)
});
func 调用给了我这个 TypeScript 错误:
Parameter 'ctx' implicitly has an 'any' type.ts(7006)
我希望
ctx
根据元组的第一个元素自动具有类型“foo”或“bar”。但是,TypeScript 无法正确推断 ctx
的类型。如何修改我的函数,以便 TypeScript 根据元组正确推断回调参数的类型?
您遇到了 microsoft/TypeScript#55632 中报告的 TypeScript 错误。 元组类型的可区分联合似乎不支持回调参数的上下文类型。这似乎是因为上下文类型在数组文字中的工作方式与在对象文字中的工作方式不同。事实上,如果您更改代码以使用对象类型的可区分联合,问题就会消失:
const func = <T extends object>(arg: {
[N in keyof T]: {
[K in TInput]: { k: K, f: (arg: K) => void }
}[TInput]
}) => { }
func({
flash: { k: 'bar', f: (ctx) => { } }, // ctx inferred as "bar"
antman: { k: 'foo', f: (ctx) => { } }, // ctx inferred as "foo"
});
如果您希望它适用于元组,那么您需要解决它。对于您的特定示例,一种方法是完全放弃联合。现在你的
T
类型没有任何属性值,它只是键。我们可以更改它,以便属性值对应于第一个元组元素的类型。所以在你的例子中它将是{flash: "bar", antman: "foo"}
。然后我们可以让 TypeScript 从参数中推断出 T
,然后使用 that 进行上下文输入:
const func = <T extends Record<keyof T, TInput>>(arg: {
[K in keyof T]: [T[K], (arg: T[K]) => void]
}) => { }
func({
flash: ['bar', (ctx) => { }], // ctx inferred as "bar"
antman: ['foo', (ctx) => { }], // ctx inferred as "foo"
});
这是可行的,因为
flash
属性的类型只是 ["foo", (arg: "foo") => void]
而不是并集。因此,我们回避了 microsoft/TypeScript#55632 中的错误。