Præcise typer til en opslag af værdier af en strengenum i TypeScript
Antag, at jeg har denne strengenum:
enum Color {
None = "Q",
Red = "R",
Green = "G",
Blue = "B",
}
Nu har string enums i TypeScript ingen omvendte kortlægninger, så jeg kan ikke skrive Color["Q"] for at få en potentiel Color tilbage. Jeg prøvede at erklære min egen hjælpefunktion:
type StringEnum = {[key: string]: string};
function lookup<E extends StringEnum>(stringEnum: E, s: string): keyof E | undefined {
for (const enumValue of keysOf(stringEnum)) {
if (stringEnum[enumValue] === s) {
return enumValue;
}
}
return undefined;
}
med keysOf som
function keysOf<K extends {}>(o: K): (keyof K)[];
function keysOf(o: any) { return Object.keys(o); }
… men faktisk ved jeg ikke, hvad returtypen skal være. Dette er ment til at kompilere, men gør det ikke:
const color: Color = lookup(Color, "Q") || Color.None;
fordi returtypen for lookup-funktionen nu er "None" | "Red" | "Green" | "Blue" | undefined i stedet for bare Color | undefined.
Kan dette løses godt samtidig med, at typeinformationen bevares?
Svar #1
Din lookup-funktion returnerer en Enum-streng nøgle, men du giver også Enum-værdi Color.None som en kortslutning, hvis lookup returnerer undefined. Så typerne passer ikke her.
Generelt kan streng-Enum nøgler typisk skrives sådan her:
type ColorKeys = keyof typeof Color // "None" | "Red" | "Green" | "Blue"
const blueKey: keyof typeof Color = "Blue"
Hvis du skriver const blueValue=Color.None, holder variablen blueValue Enum-strengværdien "Q", ikke den bogstavelige "None". Så vi kan rette const color-tildelingen på følgende måde:
const color = lookup(Color, "Q") || "None";
// or with explicit type
const color: keyof typeof Color = lookup(Color, "Q") || "None";
Enum-typer kan ikke have en indeks-signatur, så jeg typede StringEnum som {[key: string]: any} her for at få det til at kompilere.
Legeplads
Opdatering: få skrevet enum-værdi fra lookup
Hvis du vil videregive en ikke-indsnævret strengværdi til lookup og få den tilbage som en indsnævret enum-værdi type, hvis den findes (ellers undefined), kan du gøre det på en måde, der ligner dit første eksempel:
function lookup<E extends StringEnum>(
stringEnum: E,
s: string
): E[keyof E] | undefined {
for (const enumKey of keysOf(stringEnum)) {
if (stringEnum[enumKey] === s) {
// here we have to help the compiler
return stringEnum[enumKey] as E[keyof E];
}
}
return undefined;
}
// let's test it
const look = lookup(Color, "Q") // const look: Color | undefined
// for further understanding
type AllColorKeys = keyof typeof Color // = "None" | "Red" | "Green" | "Blue"
type AllColorValues = (typeof Color)[keyof typeof Color] // = Color
type IsColorSuperType = Color.Blue extends Color ? true: false // = true
I tilfælde af Color enum vil den returnerede opslagstype give dig værdien Q med typen Color | undefined, da unionen af alle mulige Color enum-værdier Color.None, Color.Red og så videre vil være af supertypen Color. Den definerede enum-konstruktion selv har typen typeof Color, sammenlignelig med den statiske og instansside af en klasse.
Legeplads
