typescript - Kan ikke anvende reduce på array af tal eller nuller
Jeg legede med TS, her ville jeg bare have en metode, der ville summere tal (forudsat at nogle elementer også kunne være null, så de ville blive ignoreret):
let squareNumbers = (numbers: (number | null )[]): number => {
return numbers.reduce((accumulator,c)=> {
return c === null ? accumulator : accumulator+c
}, 0);
}
console.log(squareNumbers([1,2, null, 4,5]))
Forventet output: 12
Men jeg får fejl:
Type 'number | null' kan ikke tildeles typen 'number'. Type 'null' kan ikke tildeles typen 'number'
Ikke sikker på, hvad det klager over? Jeg troede, jeg havde nogle tjek for det som c === null og brug af 0 som accumulator's startværdi.
Opdatering: Ok, jeg tror, at klagen handlede om typen af det, jeg returnerede fra hovedfunktionen, efter at have ændret ovenstående kode til dette:
let squareNumbers = (numbers: (number | null )[]) => { // removed return type
return numbers.reduce((accumulator,c)=> {
return c === null ? accumulator : accumulator+c
}, 0) ;
}
Nu klager det over, at accumulator kan være null. Men burde det ikke udlede, at det ikke kan være null i mit eksempel?
Svar #1
Typescript definerer arrays som
interface Array<T> {
...
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: readonly T[]) => T, initialValue: T): T;
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: readonly T[]) => U, initialValue: U): U;
}
I din kode er numbers-arrayet (numbers: (number | null )[]) Array<number | null> og T er number | null.
Typescript skal tjekke, hvilken overload af reduce du mener. Det prøver sandsynligvis den første definition først og vil se, at der ikke er nogen typeovertrædelse. Alle parametre er number | null, eller underordnede typer af disse, nemlig null eller number eller typen 0, som er en underordnet type af number.
Så det vælger
type T = number | null
reduce(cb: (accumulator: T, c: T) => T, initial: T): T
og så klager over, at du ikke kan gøre accumulator+c, fordi accumulator kunne være null.
Det du vil bruge i din kode er dog den anden overbelastning, der kortlægger fra arrayets type T til en hvilken som helst anden type U under reduce-operationen.
Ved at kalde det med
numbers.reduce((accumulator: number,
du opretter en sag, hvor callbacken ikke længere accepterer number | null i accumulator, så den overbelastning ikke længere er gyldig. I stedet prøver den den anden og vil opdage det
type T = number | null
type U = number
reduce(cb: (accumulator: U, c: T) => U, initial: U) => U
virker uden overtrædelse.
Det andet alternativ er eksplicit at definere U ved at kalde det som
reduce<number>((accumulator, c) => ..., 0)
Skiftet mellem disse overbelastninger fungerer normalt uden at angive typen, men i dette tilfælde har du typer, der tilfældigvis er for kompatible med hinanden.
Svar #2
Du skal også angive typen for reduce callback
let squareNumbers = (numbers: (number | null)[]): number => {
return numbers.reduce((accumulator: number, c: number | null) => {
return c === null ? accumulator : accumulator + c
}, 0);
}
Du kan altid gøre noget som dette i callbacken
let squareNumbers = (numbers: (number | null )[]) => { // removed return type
return numbers.reduce((accumulator, c)=> {
if (accumulator === 2) {
return null;
}
return c === null ? accumulator : accumulator+c
}, 0) ;
}
Derfor klager compileren over, at accumulator kan være null.
