Array som inneholder alle alternativer av typen verdi i Typescript
Her er kode som tvinger alle alternativer av type til å være til stede i nøklene til object (og ikke tillater andre nøkler):
type Fruit = 'apple' | 'peach';
const objectWithAllFruitsAsKeys: {
[ key in Fruit ]: any
} = { apple: '', peach: '' }
Jeg leter etter en typingsformel som tillater nær oppførsel med verdier i en array — for å få array med alle verdiene av typen Fruit som er til stede i den som verdier.
const arrayWithAllFruitsAsvalues: ??? = ['apple', 'peach'];
Har dere noen ideer? Takk på forhånd!
Svar #1
Hvis du definerer arrayet først, her er en måte å få Fruit-typen på
const arrayWithAllFruitsAsvalues = ['apple', 'peach'] as const;
type Fruit = typeof arrayWithAllFruitsAsvalues[number]; // "apple" | "peach"
const objectWithAllFruitsAsKeys: {
[ key in Fruit ]: any
} = { apple: '', peach: '' }
Svar #2
Typesystemet er ikke spesielt rettet mot å garantere at en gitt tuppel har nøyaktig de samme verdiene og antall elementer som medlemmet av en gitt unionstype. Det finnes alle slags måter å prøve å få dette til å skje på.
Jeg vil presentere en måte: lag en hjelpefunksjon som tar en unionstype (som Fruit) og returnerer en array bygger som ber utvikleren om å legge til hvert enkelt medlem av unionen, og på hvert trinn fjerner den de tidligere la til medlemmene fra listen over mulige neste elementer, og til slutt lar deg bare bygge arrayen. Dette fungerer egentlig bare for en union av literaltyper (eller andre enhetstyper som kun har én mulig verdi hver).
Her er den (og du kunne putte den i et bibliotek et sted hvis den er for stygg til å se på)
// TS4.0+
type Push<T extends readonly any[], V> = [...T, V];
type ReadonlyTuple<T> = Extract<Readonly<T>, readonly any[]>;
interface TupleBuilderAddable<U, T extends readonly any[]> {
add<V extends U>(v: V): [U] extends [V] ?
TupleBuilderBuildable<ReadonlyTuple<Push<T, V>>> :
TupleBuilderAddable<Exclude<U, V>, ReadonlyTuple<Push<T, V>>>;
}
interface TupleBuilderBuildable<T extends readonly any[]> {
build(): T;
}
function tupleBuilder<U>():
[U] extends [never] ?
TupleBuilderBuildable<[]> :
TupleBuilderAddable<U, []> {
const tuple: any[] = [];
const ret = {
add(v: any) {
tuple.push(v);
return ret;
},
build() {
return tuple;
}
}
return ret as any;
}
«Kort» forklaring:
Push<T, V>tar en tuppeltypeTog en typeVog lager en ny tuppel medVlagt til på slutten avT.TupleBuilderBuildable<T>tar en tuplet-typeTog representerer et objekt med enbuild()-metode som returnerer en verdi av typenT.TupleBuilderAddable<U, T>tar en unionstypeUog en tuppeltypeTog representerer et objekt med enadd()-metode. Denneadd()-metoden aksepterer en verdi av typenVsom utviderU. Konseptuelt vil den fjerneVfraUfor å få en ny unionstypeU', og deretter leggeVtilT-tuplen for å få en ny tuppeltypeT'. HvisU'er tom, returnereradd()TupleBuilderBuildable<T'>. Ellers returnerer denTupleBuilderAddable<U', T'>. Dette er det viktige trinnet i typesystemet: hver gang du kalleradd(), flytter det ett element fra unjonen til tuplen.
Ved kjøretid har du bare en funksjon som returnerer et objekt med både en add()- og en build()-metode som henholdsvis legger til i et array og returnerer et array. Men ved kompileringstid er bare add() eller build() eksponert, avhengig av om det finnes noe igjen i unionstypen som du ikke har brukt.
Nok forklaring; her er hvordan du bruker det:
type Fruit = 'apple' | 'peach';
const t = tupleBuilder<Fruit>().add("apple").add("peach").build();
// const t: readonly ["apple", "peach"]
IntelliSense vil bare la deg gjøre visse ting:
const builder0 = tupleBuilder<Fruit>();
// const builder0: TupleBuilderAddable<Fruit, []>
// builder0 only has an add() method
builder0.add("cherry"); // error! "cherry" is not assignable to Fruit.
const builder1 = builder0.add("peach"); // okay
// const builder1: TupleBuilderAddable<"apple", readonly ["peach"]>
// builder1 only has an add method
builder1.add("peach"); // error! "peach" is not assignable to "apple".
const builder2 = builder1.add("apple"); // okay
// const builder2: TupleBuilderBuildable<readonly ["peach", "apple"]>
// builder2 only has a build method
const u = builder2.build();
// const u: readonly ["peach", "apple"]
Det tvinger deg til å add() "apple" og "cherry" i en viss rekkefølge, og deretter build(). Ja, det er ordentlig langt, men kompilatoren gir deg hint hele veien. Du kan ikke kalle build() før du har lagt til alle elementene, og du kan ikke kalle add() når du har gjort det, og du kan ikke legge til feil eller gjentatte elementer. Og når du er ferdig, har du en tuple-type.
Ikke sikker på om dette passer til ditt bruksområde, men jeg ville bare vise en mulighet. Lykke til!
Lenke til kode
