Typer fra både nøgler og værdier af objekt i Typescript
Jeg har to sæt af strengværdier, som jeg ønsker at kortlægge fra det ene til det andet som et konstant objekt. Jeg vil generere to typer ud fra den kortlægning: én for nøgler og én for værdier.
const KeyToVal = {
MyKey1: 'myValue1',
MyKey2: 'myValue2',
};
Nøglerne er nemme nok:
type Keys = keyof typeof KeyToVal;
Jeg har problemer med at få en compile-time-type for værdierne. Jeg tænkte, at måske en af disse ville virke:
type Values = typeof KeyToVal[Keys];
type Values<K> = K extends Keys ? (typeof KeyToVal)[K] : never;
type Prefix<
K extends Keys = Keys,
U extends { [name: string]: K } = { [name: string]: K }
> = {[V in keyof U]: V}[K];
Alt dette gjorde bare Values til string. Jeg prøvede også at tilpasse de to svar fra How to infer typed mapValues using lookups in typescript?, men enten fik jeg mine tilpasninger forkert, eller også passede svarene ikke til min situation i første omgang.
Svar #1
Compileren vil udvide strenglitteral typen til string, medmindre nogle specifikke betingelser er opfyldt som forklaret i GitHub issues og PR, eller const-assertion bruges for litteralværdi. Const-assertions dukkede op i TypeScript 3.4:
const KeyToVal = {
MyKey1: 'myValue1',
MyKey2: 'myValue2',
} as const;
type Keys = keyof typeof KeyToVal;
type Values = typeof KeyToVal[Keys]; // "myValue1" | "myValue2"
Før version 3.4 var der en løsning for at opnå den samme effekt. For at få compileren til at udlede litterale typer, skulle du sende dit objekt gennem en funktion med passende udformede generiske typeparametre, denne ser ud til at gøre tricket for dette tilfælde:
function t<V extends string, T extends {[key in string]: V}>(o: T): T {return o}
Hele formålet med denne funktion er at fange og bevare typer for at muliggøre typeinferens, det er helt ubrugeligt ellers, men med det kan du have
const KeyToVal = t({
MyKey1: 'myValue1',
MyKey2: 'myValue2',
});
type Keys = keyof typeof KeyToVal;
type Values = typeof KeyToVal[Keys]; // "myValue1" | "myValue2"
Svar #2
Faktisk bør du ændre KeyToVal til nedenstående erklæring:
const KeyToVal = {
MyKey1: 'myValue1',
MyKey2: 'myValue2',
} as const; // <----- add the <as const> here
Opret derefter nøgletyperne:
type Keys = keyof typeof KeyToVal;
Nu kan du oprette typerne af værdierne:
type ValuesTypes = typeof KeyToVal[Keys];
Svar #3
Du forsøger at udlede typen fra objektet (som kan have et vilkårligt antal nøgler/værdier). Du kan prøve først at beskrive typen (eller måske bedre en grænseflade) og derefter udlede Nøgler og Værdier sådan her:
type KeyToObjMap = {
some: "other",
more: "somemore",
};
type Keys = keyof KeyToObjMap;
type Values = KeyToObjMap[Keys];
let one: Values = "some";
let two: Values = "other";
let three: Keys = "some";
let four: Values = "somemore";
let five: Keys = "fun";
Og du vil have en korrekt markering i IDE.
Svar #4
Ikke helt det samme, men hvis du har et array af objekter i stedet for et enkelt objekt, kan du hente værdierne af en kendt egenskab for at oprette en type ved at gøre noget som dette:
const keyToValArray = [
{ value: 'myValue1', label: 'myLabel1' },
{ value: 'myValue2', label: 'myLabel2' }
] as const;
type Keys = typeof keyToValArray[number]['value']; // 'myValue1' | 'myValue2'
