考虑以下片段:
type Add = (a: number | string, b: number | string) => number | string;
function hof(cb: Add) {}
const addNum = (a: number, b: number): number => a + b;
const addStr = (a: string, b: string): string => a + b;
// @ts-expect-error
hof(addNum);
// @ts-expect-error
hof(addStr);
Run Code Online (Sandbox Code Playgroud)
为什么我们不能将addNum和addStr函数传递给hof,IMO 他们的调用签名应该与hof预期兼容,但实际上并非如此。
如果它们的类型不兼容,那么为什么以下代码片段中的重载签名不会抱怨?
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: number | string, b: number | string): number | string {
return 1;
}
Run Code Online (Sandbox Code Playgroud)
您假设协变类型,但函数参数是逆变的。(有关这方面的一些深度,请参阅这篇文章。)
将您的示例稍微更改一下,您就会开始看到问题:
type Add = (a: number | string, b: number | string) => number | string;
function hof(cb: Add) {
return cb
}
const addNum = (a: number, b: number): number => Math.round(a) + Math.round(b);
const addStr = (a: string, b: string): string => `${a.toLowerCase()}${b.toLowerCase}`;
hof(addNum)('a', 'b'); // should error because addNum can't take strings
hof(addStr)(123, 456); // should error because addStr can't take numbers
Run Code Online (Sandbox Code Playgroud)
这里hof返回你传递给它的回调函数。所以如果你通过它addNum那么字符串就会崩溃。如果你通过了它,addStr那么数字就会崩溃。
因此,对于可分配给函数类型的函数,它必须采用所有参数类型的超集而不是子集。
但是,如果您使用hof泛型,则可以从参数创建函数类型。例如:
type Add<T extends number | string> = (a: T, b: T) => T;
function hof<T extends number | string>(cb: Add<T>): Add<T> {
return cb
}
const addNum = (a: number, b: number): number => Math.round(a) + Math.round(b);
const addStr = (a: string, b: string): string => `${a.toLowerCase()}${b.toLowerCase}`;
hof(addNum)(123, 456); // fine
hof(addStr)('a', 'B'); // fine
hof(addNum)('a', 'b'); // should error because addNum can't take strings
hof(addStr)(123, 456); // should error because addStr can't take numbers
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
97 次 |
| 最近记录: |