Типизированные массивы и типы объединения - PullRequest
6 голосов
/ 04 июля 2019

Я много работаю с типизированными массивами, и многие мои функции действительно должны работать с любым типом массивов (например, суммируя 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)

Согласно документам :

Если у нас есть значение, имеющее тип объединения, мы можем получить доступ только к тем элементам, которые являются общими для всех типов в объединении.

То, как я здесь использую reduce, является общим для всех массивов, но я предполагаю, что проблема заключается в необязательном 4-м аргументе (который для Uint8Array.prototype.reduce равен Uint8Array, а для Int8Array.prototype.reduce равен Int8Array ).

Есть ли простой обходной путь для этого? Или мне нужно написать обобщенную реализацию для каждого из map, reduce, filter?

Ответы [ 2 ]

2 голосов
/ 04 июля 2019

Всегда возникала проблема при вызове объединений функций.До недавнего времени правилом было то, что никакие вызовы вообще не допускались, но так как вызов 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 голос
/ 04 июля 2019

Есть простой обходной путь. Объявить интерфейс с общей сигнатурой метода

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 и объединить их.

...