类型化数组和联合类型

Typed arrays and union types

我经常使用类型化数组,我的很多函数确实应该能够使用任何类型的类型化数组(例如,对 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:

If we have a value that has a union type, we can only access members that are common to all types in the union.

我在这里使用 reduce 的方式对所有数组都是通用的,但我推测问题是可选的第 4 个参数(对于 Uint8Array.prototype.reduceUint8Array 但对于Int8Array.prototype.reduce 是一个 Int8Array).

有没有简单的解决方法?或者我是否需要为每个 mapreducefilter 编写泛型实现?

有一个简单的解决方法。使用公共方法签名声明接口

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并组合它们。

调用函数的联合一直存在问题。直到最近,规则还是根本不允许调用,但自 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

这确实意味着数组类型的联合很难使用,并且只允许调用非常少的方法集(大多数数组方法都具有泛型类型参数)。

这也适用于 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)
}