typescript2.0 - Använd strängen enumvärde i TypeScript-gränssnitt som en beräknad egenskapsnyckel
Jag vill definiera en funktion som returnerar olika typer av objekt baserat på vilken nyckel jag anger. Det är i princip som tricket som används här i createElement-funktionen
https://github.com/Microsoft/TypeScript/blob/master/lib/lib.dom.d.ts#L3117
Men istället för en strängliteral vill jag använda en strängenumtyp istället. Så jag skrev något som detta
class Dog {}
class Cat {}
class Bird {}
enum Kind {
Dog = 'Dog',
Cat = 'Cat',
Bird = 'Bird'
}
interface KindMap {
[Kind.Dog]: Dog
[Kind.Cat]: Cat
[Kind.Bird]: Bird
}
function getAnimal<K extends keyof KindMap> (key: K): KindMap[K] {
switch (key) {
case Kind.Dog:
return new Dog()
case Kind.Cat:
return new Cat()
case Kind.Bird:
return new Bird()
}
}
Men TypeScript verkar inte gilla sättet jag satte värdet för enum Kind i gränssnittet som en beräknad egenskap, det klagar
A computed property name in an interface must directly refer to a built-in symbol.
Här kommer frågan, jag har redan konstanterna definierade i enum, jag gillar inte att använda strängliteraler, finns det ett sätt jag kan få detta att fungera? Vilket betyder att använda Kind-enums värde som nyckel för den beräknade egenskapen i KindMap.
Svar #1
Ibland är ett enkelt objekt med keyof enklare än ett enum i TypeScript:
class Dog { }
class Cat { }
const kindMap = {
Dog,
Cat
};
type KindMap = typeof kindMap;
function getAnimalClass<K extends keyof KindMap>(key: K): KindMap[K] {
return kindMap[key];
}
// These types are inferred correctly
const DogClass = getAnimalClass('Dog');
const dog = new DogClass();
const CatClass = getAnimalClass('Cat');
const cat = new CatClass();
Prova det i TypeScript Playgrounds
Jag försökte implementera getAnimal() med mappade typer men verkade stöta på buggig inferens. Men att slå upp klassen var enklare.
Att inline:a getAnimalClass-uppslaget fungerar också med typavledning:
const dog = new kindMap['Dog']();
Svar #2
Den ursprungliga koden fungerar nästan som den är i TypeScript 2.7.2, även om den har ett fel eftersom andra värden än de 3 Kind kan skickas in.
Du kan säga att det är möjligt att returnera undefined:
function getAnimal<K extends keyof KindMap>(key: K): undefined | KindMap[K] {
switch (key) {
case Kind.Dog:
return new Dog()
case Kind.Cat:
return new Cat()
case Kind.Bird:
return new Bird()
default:
return undefined;
}
}
Jag skulle förmodligen bara använda en karta från enum till klasskonstruktören direkt, eftersom användning av den enkelt kan härleda typen:
const kindMap = {
[Kind.Dog]: Dog,
[Kind.Cat]: Cat,
[Kind.Bird]: Bird,
}
const tweeter = new kindMap[Kind.Bird]();
Om du vill att funktionen ska instansiera objekten kan du också ställa in ett parallellt gränssnitt:
interface KindMap {
[Kind.Dog]: Dog,
[Kind.Cat]: Cat,
[Kind.Bird]: Bird,
}
const getAnimal = <K extends Kind>(k: K): KindMap[K] => new kindMap[k]();
const meower = getAnimal(Kind.Cat);
