我正在定义一个以接口为参数的泛型函数。
此接口具有多个字段,其中一个字段是通用参数的键。
另一个字段也采用了此通用参数的键,但是我想强制两者相等,而无需用户明确指定它们。
这是一个具体的最小示例:
// Generic interface representing the parameter of the generic function
interface Inte<DataType extends object, K extends keyof DataType = keyof DataType> {
key: K;
fn: (val: DataType[K]) => void;
}
// Define the generic function
function test<DataType extends object>(arg: Inte<DataType>);
// Testing interface
interface Base {
first: string;
second: number;
}
// Testing function call
test<Base>({
key: "first",
fn: (val: number) => {}, // Error on 'fn', here
});
以上代码在fn
的定义行报告了一个错误,指出参数类型(number
)无法分配给string
,因为它确实推断出了我的第二个参数通用接口K
仍然是string | number
,尽管key
应该可以帮助它找到正确的类型。
整个错误是(不知道为什么在其中放置一个随机的ReactText
。也许是因为我在.tsx
文件中尝试了这个错误?):
Type '(val: number) => void' is not assignable to type '(val: ReactText) => void'.
Types of parameters 'val' and 'val' are incompatible.
Type 'ReactText' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.ts(2322)
我终将收到一条错误消息,指出参数必须为string
而不是number
。 (如果我更改为val: string
,也完全没有错误。
这个问题有解决方案吗?我知道我可以做"strictFunctionTypes": false
,但是那会削弱我在那里的类型检查。
我很确定我可以做些可靠的事情来解决这个问题!
顺便说一句,如果它可能以任何方式相关,那么实际代码发生在React使用的上下文中,这意味着我不能更改通用函数的参数,因为它是一个组件。
答案 0 :(得分:0)
问题出在您对Inte
的定义中。您给K
设置了默认值keyof DataType
,并且没有在function test<DataType extends object>(arg: Inte<DataType>)
中指定固定值,这意味着它将/ always /默认为keyof DataType
,即使如果您在牢记key
的情况下调用该函数。解决此问题的一种方法是使test
需要K
的类型:
declare function test<DataType extends object, K extends keyof DataType>(arg: Inte<DataType, K>);
// Testing function call
test<Base, "first">({
key: "first",
fn: (val: number) => { },
/*
ERR on fn:
Type '(val: number) => void' is not assignable to type '(val: string) => void'.
Types of parameters 'val' and 'val' are incompatible.
Type 'string' is not assignable to type 'number'
*/
});
现在,如果您编写(val: string)
,它将可以正常工作。显然这是不符合人体工程学的,因为您需要两次写下“ first”。不过,很难避免这种情况,因为DataType
已经是一种浮点型参数,例如它更多是一种约束,而不是可以从输入中得出的东西。不幸的是,在TS中,一旦指定了一个通用参数,就必须指定它们的 all 。
编辑:部分参数推断的一种常见解决方法是使用咖喱函数:
function test<DataType extends object>() {
return function innerTest<K extends keyof DataType>(arg: Inte<DataType, K>) {
throw new Error("Not yet implemented");
}
};
test<Base>()({
key: "first",
fn: (val: number) => { },
// still an err, but changing to `val: string` makes it work
});
现在test<DataType>()
实际上创建了一个约束为DataType
的测试器函数。返回的函数是对K
的推断。