我经常使用类型数组,而我的许多函数确实应该能够使用任何类型的类型数组(例如,将Uint8Array
或Float32Array
求和)。有时候,我只需要一个简单的类型并集就可以逃脱,但是经常我会遇到相同的错误。
一个简单的例子:
type T1 = Uint8Array;
type T2 = Int8Array;
type T3 = Uint8Array | Int8Array;
// No problems here:
const f1 = (arr: T1) => arr.reduce((sum, value) => sum + value);
const f2 = (arr: T2) => arr.reduce((sum, value) => sum + value);
// Does not work:
const f3 = (arr: T3) => arr.reduce((sum, value) => sum + value);
f3
上的错误是:
Cannot invoke an expression whose type lacks a call signature. Type '
{
(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number): number;
(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number, initialValue: number): number;
<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Uint8Array) => U, initialValue: U): U;
} | {
(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number): number;
(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number, initialValue: number): number;
<U>(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: Int8Array) => U, initialValue: U): U;
}' has no compatible call signatures.ts(2349)
根据docs:
如果我们拥有一个具有联合类型的值,则我们只能访问该联合中所有类型都通用的成员。
我在这里使用reduce
的方式在所有数组中都是通用的,但是我想问题是可选的第4个参数(对于Uint8Array.prototype.reduce
是Uint8Array
,而对于{{ 1}}是Int8Array.prototype.reduce
)。
是否有一个简单的解决方法?还是我需要为Int8Array
,map
,reduce
中的每一个编写泛型实现?
答案 0 :(得分:2)
调用函数联合一直存在问题。直到最近,该规则还是禁止任何调用,但是自从3.3(PR)开始以来,存在一些警告。您在此处遇到的最大问题是,如果联合会的组成部分都具有通用签名,则仍将不允许该调用。因此,例如在简单数组上可以调用一个forEach
(上面没有通用类型参数),而不能调用reduce
(因为reduce
中的string[]
和来自number[]
的具有通用类型参数):
declare let o: string[] | number[];
o.forEach((e: number | string) => console.log(e)); // ok
o.reduce((e: number | string, r: number | string) => e + ' ' + r) //err
这确实意味着很难使用数组类型的并集,并且仅允许调用非常小的一组方法(大多数Array方法具有泛型类型参数)。
这也适用于Uint8Array
和Int8Array
,尽管它们不继承数组,但它们具有大多数相同的方法。
这里没有好的解决方案,最简单的解决方法是将变量声明为一种类型并进行处理(假设您不使用array
回调参数应该没问题)>
const f3 = (arr: T3) => (arr as Uint8Array).reduce((sum, value, array /* no good inferred to Uint8Array */) => sum + value);
或者退回到您可以调用的功能之一
const f4 = (arr: T3) => {
let sum = 0;
(arr as Uint8Array).forEach((val)=> sum + val)
}
答案 1 :(得分:1)
有一个简单的解决方法。声明具有通用方法签名的界面
type T3 = Uint8Array | Int8Array;
interface HasReduce {
reduce(c: (p: number, n: number) => number): number; // common callback signture with 2 arguments
}
function someLogic(arr: HasReduce): number {
return arr.reduce((sum, value) => sum + value);
}
declare var v : T3;
someLogic(v); // OK
因此您可以声明HasMap,HasFilter并将它们组合起来。