lambda - Tipurile de uniune Typescript array `a[] | b[]` nu pot fi `reduce`te, dar pot fi `map`uite
Încerc să folosesc reduce peste o uniune de tipuri de array: foo[] | bar[]. Cu map funcționează așa cum mă aștept și obțin un element de tip foo|bar în lambda, dar nu funcționează cu reduce. Primesc eroarea că 'exprimarea nu poate fi apelată, deoarece semnăturile nu sunt compatibile'.
Poate că asta are legătură, dar asta era pe vremea TS 3.6, iar eu folosesc 4.5. Există o modalitate (curată) de a o ocoli?
Am un Playground TypeScript aici
const m = [] as Array<number | string>;
m.reduce((it) => it); // ok
m.map((it) => it); // ok
const m2 = [] as Array<number> | Array<string>;
m2.reduce((it) => it);
m2.reduce((it) => it as any); // not even with 'any'-lambda
// This expression is not callable.
// Each member of the union type '<OMITTED> ...' has signatures, but none of those signatures are compatible with each other.
m2.map((it) => it); // why does `map` work then ?
// same with tuples
const m3 = [1, 1] as [number, number] | [string];
m3.map((it) => it);
m3.reduce((it) => it); // same error
Răspuns #1
În plus față de comentariul lui jcalz, aceasta este o restricție cunoscută a TS, dar există o soluție prin care mutați predicatul de tip job la inferență generică în loc de un întreg predicat de tip al cărui tip de array este
function map<T extends number | string>(arr: T[]) {
return arr.map(v => v); // no error
}
function reduce<T extends number | string>(arr: T[]) {
return arr.reduce(it => it); // no error
// return arr.reduce((acc, cur) => acc + (typeof cur === "number" ? cur : cur.length), 0); // no error either, with some practical logic
}
Răspunsul #2
În plus față de comentariul lui jcalz, am găsit următoarele.
Definiții TypeScript
Semnătura funcției pentru map este
export function map<T, U>(iterable: Iterable<T>, mapper: (value: T, index: number, iterable: Iterable<T>) => U): U[];
Are un tip de returnare diferit U decât intrarea sa T.
Una dintre semnăturile funcției pentru reduce este
export function reduce<T>(
iterable: Iterable<T>,
reducer: (previousValue: T, currentValue: T, currentIndex: number, iterable: Iterable<T>) => T,
initialValue?: T
): T;
Are același tip de returnare ca și inputul său (T). Problema apare când se atribuie tipul înapoi lui însuși. Typescript nu știe (și nu ar trebui să știe) ce tip să folosească.
O soluție este să verifici tipul înainte de a folosi reduce, cu un gardian de tip, astfel,
const isNumberArray = (arr: unknown[]): arr is number[] => {
for (let i = 0; i < arr.length; i++) {
const element = arr[i]
if (typeof element !== 'number') {
return false
}
}
return true
}
Teren de joacă
