类型化数组和联合类型

时间:2019-07-04 08:50:26

标签: javascript typescript typescript-typings typed-arrays

我经常使用类型数组,而我的许多函数确实应该能够使用任何类型的类型数组(例如,将Uint8ArrayFloat32Array求和)。有时候,我只需要一个简单的类型并集就可以逃脱,但是经常我会遇到相同的错误。

一个简单的例子:

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.reduceUint8Array,而对于{{ 1}}是Int8Array.prototype.reduce)。

是否有一个简单的解决方法?还是我需要为Int8Arraymapreduce中的每一个编写泛型实现?

2 个答案:

答案 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方法具有泛型类型参数)。

这也适用于Uint8ArrayInt8Array,尽管它们不继承数组,但它们具有大多数相同的方法。

这里没有好的解决方案,最简单的解决方法是将变量声明为一种类型并进行处理(假设您不使用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并将它们组合起来。