reactjs - Argument av typ 'number' kan inte tilldelas till parameter av typ 'never' i array.includes()


Jag får ett Typescript-fel,

fel TS2345: Argument av typen 'number' kan inte tilldelas parametern av typen 'string & number'. Typen 'number' kan inte tilldelas typen 'string'. 112

setProductPrices(productPrices.filter(
  (p): boolean => !selectedProductIds.includes(p.id)));
                                               ^^^^

från följande kod:

export interface IProductPrice {
    id: number;
}
const [productPrices, setProductPrices] = useState<IProductPrice[]>([]);
const [selectedProductIds, setSelectedProductIds] = useState<string[] | number[]>([]);

const deleteSelectedProducts = (): void => {
        setProductPrices(productPrices.filter((p): boolean => !selectedProductIds.includes(p.id)));
        setSelectedProductIds([]);
};

Metoden includes() förväntas tydligen en parameter av typen 'never' medan p.id är ett nummer. Vet någon hur man kan fixa detta?

Svar #1

Problemet

När du har en förening av arrayer av olika typer kan du inte anropa metoder på dem.

Anledningen

Du har string[] | number[], och därför är .filter:

  ((filterFunc: (x: string) => boolean) => string[]) 
| ((filterFunc: (x: number) => boolean) => number[])

När TS kombinerar de signaturerna kommer det att förena de två filterFunc-signaturerna tillsammans:

  ((x: number) => boolean) 
| ((x: string) => boolean)

Detta är lite ologiskt, men detta förenklas till (x: number & string) => boolean. För om du har en funktion som tar X, eller en funktion som tar Y, är det enda säkra att skicka något som är både X och Y, eller X & Y.

number & string är dock en omöjlig typ, som "förenklas" till never. Därför är signaturen (x: never) => boolean.

Lösningen

Idealt sett skulle du bara använda string[] eller number[] men inte båda. (Bara genom att titta på koden är det lite mystiskt varför id bara kan vara ett nummer, men "valda produkt-id" också kan vara strängar)

Men om du behöver stödja både strängar och siffror, är det enklaste sättet här att använda Array<string | number> istället för string[] | number[]: den enskilda array-typen har inte problemet med att försöka slå ihop två .filter-signaturer.

Du kan antingen ändra ditt tillstånd till att vara av den typen:

const [selectedProductIds, setSelectedProductIds] = useState<Array<string | number>>([]);

Detta är enkelt, men har nackdelen att det tillåter arrayer med blandade strängar och nummer, vilket kanske inte är önskvärt. (t.ex. setSelectProductIds(['0', 1, '2'])

Om inte, kan du tillfälligt kasta till Array<string | number>, göra filtret, och sedan kasta tillbaka till string[] | number[]. Detta är inte superrent, men borde vara säkert:

setProductPrices(
    (productPrices as Array<string | number>).filter((p): boolean => !selectedProductIds.includes(p.id)) as (string[] | number[])
);

Svar #2

För din useState istället för

const [selectedProductIds, setSelectedProductIds] = useState<string[] | number[]>([]);

Kan du försöka

const [selectedProductIds, setSelectedProductIds] = useState< (string|number)[]>([]);

istället?

Detta beror förmodligen på att det saknas kod där du har satt selectedProductIds som en array av strängar. Och i din kod har du strikt definierat det som ena arrayen och inte den andra. Kanske åtgärdar ändringen ovan det. Vänligen bekräfta.

Relaterade artiklar