Typescript Record som tvinger alle verdier fra streng-litteralforeninger
TypeScript har en innebygd type Record<Key,Value> som hjelper til med å tvinge objektets nøkler og verdier. Ved å kombinere den med enum eller streng-litteral union-typer - kan du tvinge og lete etter feil i manglende nøkler. Jeg søker etter en type i TypeScript som hjelper til med å tvinge alle verdier i Record til streng-litteral unioner. Det betyr:
type ShortLetterTillC = "A" | "B" | "C";
type LongLetterTillC = "Ai" | "Bi" | "Ci";
type LongLetterTillD = "Ai" | "Bi" | "Ci" | "Di";
// TS2741: Property 'C' is missing in type '{ A: "Ai"; B: "Bi"; }' but required in type 'Record '.
const dictionaryMissingC: Record<ShortLetterTillC, LongLetterTillC> = {
A: "Ai",
B: "Bi"
};
// Works - good
const dictionaryC: Record<ShortLetterTillC, LongLetterTillC> = {
A: "Ai",
B: "Bi",
C: "Ci"
};
// Works - I want a type that will make it fail for not having "Di"
const dictionaryD: Record<ShortLetterTillC, LongLetterTillD> = {
A: "Ai",
B: "Bi",
C: "Ci"
};
const dictionaryDThatIWantToForce = {
A: "Ai",
B: "Bi",
C: "Ci",
D: "Di"
};
dictionaryD gir meg ingen feil fordi Record<ShortLetterTillC, LongLetterTillD> forteller TypeScript at det skal være et objekt med alle nøklene fra ShortLetterTillC og for hver nøkkel kan verdien være en av LongLetterTillD. Selv om dette er bra for mange brukstilfeller, finnes det noen brukstilfeller hvor jeg ønsker å være avhengig av verdiene og dekke dem alle som i et ordbok. Kan jeg ha en type som tvinger spesifisering av nøkler på Records bokstavelige strengunionverdier?
Svar #1
Jeg er ikke sikker på om du kan kreve at et objekt har et sett med verdier uten å spesifisere nøklene.
Men hvis du kjenner de tilsvarende nøklene, kan du dra nytte av kartlagte typer:
// Using mapped types
type RecordWithValues<
TValues extends string,
TKeyMapping extends Record<TValues, string>,
> = { [V in TValues as TKeyMapping[V]]: V };
type MyValues = "Ai" | "Bi" | "Ci" | "Di";
// Then we associate a specific key to each value of your string literal union
// e.g.: suppose we want to create the key with the first letter of the value
type FirstLetter<S extends string> = S extends `${infer $FirstLetter}i`
? $FirstLetter
: never;
type Test = RecordWithValues<MyValues, { [K in MyValues]: FirstLetter<K> }>;
const testOk: Test = {
A: "Ai",
B: "Bi",
C: "Ci",
D: "Di",
};
// Property 'D' with value 'Di' is missing
const testNokBecauseOfMissingValue: Test = {
A: "Ai",
B: "Bi",
C: "Ci",
};
Lekeplass
