Hur man skapar en TypeScript-typ som liknar enum där flera värden mappas till samma nyckel
Jag försöker ta en del av kodbasen som använde typen string och konvertera den till en enum-liknande typ, med TypeScript. Problemet är att i vissa fall är strängvärdena något olika. Istället för att göra två nästan identiska enum-liknande typer, finns det ett sätt att skapa en enda typ där flera värden matchar till samma nyckel?
Mitt försök ser ut så här (vilket inte fungerar):
const STATUS = {
CREATED: "CREATED",
PICKED_UP: "PICKED UP" | "PICKED_UP",
DELIVERED: "DELIVERED" | "DEL"
} as const
type ObjectValues<T> = T[keyof T];
type Status = ObjectValues<typeof STATUS>;
Jag letar efter en lösning som skulle göra detta:
console.log(STATUS.PICKED_UP === "PICKED UP" && STATUS.PICKED_UP === "PICKED_UP") //should be true
doThingWithStatus("PICKED_UP") //should be ok, assuming function expects a type Status input
Tack, allihopa!
Svar #1
Som sagt, nej, det är inte möjligt att göra det du frågar om. Du kan verkligen inte ha både STATUS.PICKED_UP === "PICKED UP" vara sant och STATUS.PICKED_UP === "PICKED_UP" vara sant, när "PICKED UP" === "PICKED_UP" är false. STATUS.PICKED_UP är något värde, och det värdet kan vara antingen eller inget av dessa strängar, men det kan inte vara båda av dessa strängar. (Och detta är förmodligen varför du har problem med att skriva ett värde som "PICKED UP" | "PICKED_UP". Hur som helst, det är bitvis ELLER-operatorn och den fungerar inte på strängar. Och det finns ingen operator som kombinerar två strängar till en enda sträng som på något sätt är lika med båda.)
Istället verkar det som att du borde refaktorera så att du mappar medlemmar av STATUS till kollektioner av strängar. Till exempel kan du använda arrayer som i
const STATUS = {
CREATED: ["CREATED"],
PICKED_UP: ["PICKED UP", "PICKED_UP"],
DELIVERED: ["DELIVERED", "DEL"]
} as const;
Då är Status unionen av elementen i ObjectValues<typeof STATUS>:
type Status = ObjectValues<typeof STATUS>[number];
// type Status = "CREATED" | "PICKED UP" | "PICKED_UP" | "DELIVERED" | "DEL"
Och din kontroll skulle vara om medlemmen av STATUS inkluderar en av strängarna i dess samling:
console.log(
STATUS.PICKED_UP.includes("PICKED UP") &&
STATUS.PICKED_UP.includes("PICKED_UP")
); // true
Du behöver inte använda arrayer; du kan använda Set, eller objekt med strängarna som nycklar.
Du skulle till och med kunna bara vända på ditt STATUS-objekt så att nycklarna och värdena byts, eftersom objekt tillåter egenskaper med olika nycklar att ha samma värde, men inte tvärtom (och jag ser inte att du föreslår att, till exempel, "DEL" ska förekomma både i STATUS.DELIVERED och, säg, STATUS.DELETED. Förmodligen har du en många-till-en eller en-till-många-relation i åtanke, inte en många-till-många):
const STATUS_REV = {
CREATED: "CREATED",
PICKED_UP: "PICKED_UP",
"PICKED UP": "PICKED_UP",
DELIVERED: "DELIVERED",
DEL: "DELIVERED"
} as const
Då är Status föreningen av nycklarna i STATUS_REV
type Status = keyof typeof STATUS_REV;
// type Status = "CREATED" | "PICKED UP" | "PICKED_UP" | "DELIVERED" | "DEL"
Och din kontroll skulle vara om den tidigare nyckeln är värdet av antingen "PICKED UP" eller "PICKED_UP"-egenskapen hos STATUS_REV:
console.log(
"PICKED_UP" === STATUS_REV["PICKED UP"] &&
"PICKED_UP" === STATUS_REV["PICKED_UP"]
); // true
Lekplatslänk till kod
