typescript - Försöker göra en keyof men begränsa giltiga egenskaper till strängtyper
Jag har stött på ett litet problem med Typescript. Jag försöker skriva några funktioner för att göra det enklare att hantera mitt Redux-tillstånd.
Jag har detta gränssnitt som jag använder för att skapa något Entity State:
export interface NormalizedState<T> {
byId: { [id: string]: T };
allIds: string[];
}
Jag har några MailState som detta:
export interface MailState extends NormalizedState<Mail> {
inbox: string[]
}
Och jag har en funktion att lägga till i byId-egenskapen så här:
type StringPropertyNames<T> = { [K in keyof T]: T[K] extends string ? K : never }[keyof T];
const appendToState = <T, K extends StringPropertyNames<T>>(s: NormalizedState<T>) => (v: T[], p: K) =>
v.reduce((acc, m) => ({ ...acc, [m[p]]: m }), s.byId);
Problemet är att jag får ett fel i appendToState-funktionen vid
[m[p]]
Jag får följande fel:
Ett beräknat egenskapsnamn måste vara av typen 'string', 'number', 'symbol' eller 'any'.
Så det verkar som om något i mina typningar är fel. All hjälp skulle uppskattas mycket!
Svar #1
Dina typer är bra, och för den som anropar bör de fungera okej. Problemet är att inuti en funktion kan Typescript egentligen inte göra så mycket med villkorliga typer. Så den kommer inte att försöka lista ut att T[K] skulle bli en string, den ser bara villkoret och ger upp.
Den enkla lösningen är att använda en typförsäkran.
const appendToState = <T, K extends StringPropertyNames<T>>(s: NormalizedState<T>) => (v: T[], p: K) =>
v.reduce((acc, m) => ({ ...acc, [m[p] as string]: m }), s.byId);
Den andra lösningen är att vända på sättet du uttrycker villkoret. Istället för att K är vilken nyckel som helst av T som har typen string, kan vi säga att T måste ha en egenskap K av typen string. Detta alternativa sätt är lättare för kompilatorn att resonera om
const appendToState2 = <T extends Record<K, string>, K extends PropertyKey>(s: NormalizedState<T>) => (v: T[], p: K) =>
v.reduce((acc, m) => ({ ...acc, [m[p]]: m }), s.byId);
