使用“类型“{}[]"的 TypeScript 拒绝不能分配给类型“数字" [英] TypeScript rejection with `Type '{}[]' is not assignable to type 'number'`
问题描述
假设我创建了一个简单的函数来将其输入加倍,
<代码>>让 f1: (x: number) =>数字 = x =>x * 2;>.type f1让 f1: (x: number) =>数字
如果我想取第一个值并将其翻倍,我可以这样做
let f2 = R.pipe( R.take(1), f1 );让 f2 = R.pipe( R.head, f1 );
这些都在 TypeScript 之外运行 f2([5,4,3])
.然而,我得到了 TypeScript,
<代码>>让 f2 = R.pipe( R.take(1), f1 );[eval].ts(6,29): 错误 TS2345: '(x: number) => 类型的参数number' 不可分配给类型为 '(x: {}[]) => 的参数数字'.参数x"和x"的类型不兼容.类型{}[]"不可分配给类型数字".>让 f2 = R.pipe( R.head, f1 );[eval].ts(6,26): 错误 TS2345: '(x: number) => 类型的参数number' 不能分配给类型为 '(x: string) => 的参数数字'.参数x"和x"的类型不兼容.字符串"类型不能分配给数字"类型.
我做错了什么.我什至不知道如何阅读,
<块引用>类型{}[]"不可分配给类型数字".
而且,我不明白是什么导致了
中的string
<块引用>类型 'string' 不能分配给类型 'number'.
(我在任何地方都没有看到 string
).
这背后的普遍问题是 TypeScript 在处理高阶泛型函数时表现不佳:https://github.com/Microsoft/TypeScript/issues/9366.
在大多数情况下,当您将通用函数(例如 R.take(n)
或 R.head
)传递给另一个(例如 R.pipe
),而不是保留免费的泛型类型参数,TypeScript 会自动推断它们.很多时候它会将这些参数推断为 {}
,它的空类型.
在带有 R.head
的第二个示例中,它推断字符串,因为 R.head
实际上定义了两个重载:
//来自 ramda/index.d.tshead(列表:ReadonlyArray):T |不明确的;头(列表:字符串):字符串;
而且由于 TypeScript 还不能决定使用哪个重载(你既没有给它一个字符串也没有给它一个数组)它只是选择一个.根据我的经验,它选择最后定义的一个,更多细节在这里:https://github.com/Microsoft/TypeScript/issues/13570
为了使您的实例正常工作,您需要手动填写通用参数以使 TypeScript 停止做出错误的猜测:
let f4 = R.pipe(R.head, f1);
对于您使用 R.take(n)
的示例,根据 Ramda,即使输入它也不应该实际工作,因为它返回一个数组,而不是一个奇异值:
//来自 ramda/index.d.ts取<T>(n:数字):{(xs: 字符串): 字符串;(xs: ReadonlyArray): T[];};
Let's say I create a simple function to double its input,
> let f1: (x: number) => number = x => x * 2;
> .type f1
let f1: (x: number) => number
If I want to take the first value and double it, I can do either
let f2 = R.pipe( R.take(1), f1 );
let f2 = R.pipe( R.head, f1 );
Both of these work f2([5,4,3])
outside of TypeScript. However, with TypeScript I get,
> let f2 = R.pipe( R.take(1), f1 );
[eval].ts(6,29): error TS2345: Argument of type '(x: number) => number' is not assignable to parameter of type '(x: {}[]) => number'.
Types of parameters 'x' and 'x' are incompatible.
Type '{}[]' is not assignable to type 'number'.
> let f2 = R.pipe( R.head, f1 );
[eval].ts(6,26): error TS2345: Argument of type '(x: number) => number' is not assignable to parameter of type '(x: string) => number'.
Types of parameters 'x' and 'x' are incompatible.
Type 'string' is not assignable to type 'number'.
What am I doing wrong. I'm not even sure how to read,
Type '{}[]' is not assignable to type 'number'.
and, I don't see what cause the string
in
Type 'string' is not assignable to type 'number'.
(I don't see a string
anywhere).
The general issue behind this is that TypeScript does really poorly with higher-order generic functions: https://github.com/Microsoft/TypeScript/issues/9366.
In the majority of situations, when you pass a generic function (such as R.take(n)
or R.head
) into another (like R.pipe
), instead of preserving free generic type parameters, TypeScript will automatically infer them. A lot of the time it infers these parameters as {}
, its empty type.
In your second example with R.head
, it infers string because R.head
is actually defined with two overloads:
// from ramda/index.d.ts
head<T>(list: ReadonlyArray<T>): T | undefined;
head(list: string): string;
and since TypeScript can't decide yet which overload to use (you've given it neither a string nor an array) it just picks one. In my experience it picks the one defined last, more details on that here: https://github.com/Microsoft/TypeScript/issues/13570
To make your instances work, you're going to need to manually fill in the generic parameters to get TypeScript to stop making the wrong guesses:
let f4 = R.pipe<number[], number, number>( R.head, f1 );
For your example with R.take(n)
, according to Ramda it shouldn't actually work even if it typed, as it returns an array, not a singular value:
// from ramda/index.d.ts
take<T>(n: number): {
(xs: string): string;
(xs: ReadonlyArray<T>): T[];
};
这篇关于使用“类型“{}[]"的 TypeScript 拒绝不能分配给类型“数字"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!