类型化数组和联合类型
Typed arrays and union types
我经常使用类型化数组,我的很多函数确实应该能够使用任何类型的类型化数组(例如,对 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:
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.reduce
是 Uint8Array
但对于Int8Array.prototype.reduce
是一个 Int8Array
).
有没有简单的解决方法?或者我是否需要为每个 map
、reduce
、filter
编写泛型实现?
有一个简单的解决方法。使用公共方法签名声明接口
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
这确实意味着数组类型的联合很难使用,并且只允许调用非常少的方法集(大多数数组方法都具有泛型类型参数)。
这也适用于 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)
}
我经常使用类型化数组,我的很多函数确实应该能够使用任何类型的类型化数组(例如,对 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:
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.reduce
是 Uint8Array
但对于Int8Array.prototype.reduce
是一个 Int8Array
).
有没有简单的解决方法?或者我是否需要为每个 map
、reduce
、filter
编写泛型实现?
有一个简单的解决方法。使用公共方法签名声明接口
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
这确实意味着数组类型的联合很难使用,并且只允许调用非常少的方法集(大多数数组方法都具有泛型类型参数)。
这也适用于 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)
}