typescript - Izvedi uniju tipova iz vrijednosti tuple/array
Recimo da imam niz:
const list = ['a', 'b', 'c']
Je li moguće iz ove vrijednosti izvesti tip unije koji je 'a' | 'b' | 'c'?
Želim ovo jer želim definirati tip koji dopušta samo vrijednosti iz statičkog niza, a također trebam navesti te vrijednosti u runtime-u, pa koristim niz.
Primjer kako se to može implementirati s indeksiranim objektom:
const indexed = {a: null, b: null, c: null}
const list = Object.keys(index)
type NeededUnionType = keyof typeof indexed
Je li moguće to učiniti bez korištenja indeksirane mape?
Odgovor #1
TRENUTNI ODGOVOR
U TypeScriptu 3.4 i novijim verzijama možete koristiti const aserciju kako biste kompajleru rekli da zadrži specifične literalne tipove svih literalnih vrijednosti u izrazu.
const list = ['a', 'b', 'c'] as const; // const assertion
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c';
Poveznica na Playground za kod
AŽURIRANJE Veljača 2019
U TypeScriptu 3.4, koji bi trebao biti objavljen u ožujku 2019., bit će moguće reći kompajleru da izvede tipuple litera kao tipuple litera, umjesto kao, recimo, string[], koristeći sintaksu as const. Ova vrsta tvrdnje uzrokuje da kompajler izvede najstroži mogući tip za vrijednost, uključujući da sve postane readonly. Trebalo bi izgledati ovako:
const list = ['a', 'b', 'c'] as const; // TS3.4 syntax
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c';
Ovo će ukloniti potrebu za bilo kakvom pomoćnom funkcijom. Još jednom sreću svima!
AŽURIRANJE srpanj 2018
Izgleda da će, počevši od TypeScript 3.0, biti moguće da TypeScript automatski zaključuje tipove tuplova. Kada bude objavljeno, funkcija tuple() koju trebate može se sažeto napisati kao:
export type Lit = string | number | boolean | undefined | null | void | {};
export const tuple = <T extends Lit[]>(...args: T) => args;
A zatim to možete koristiti ovako:
const list = tuple('a','b','c'); // type is ['a','b','c']
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'
Nadam se da će to odgovarati ljudima!
AŽURIRANJE prosinac 2017
Otkad sam objavio ovaj odgovor, pronašao sam način za izvođenje tipova torki ako ste spremni dodati funkciju u svoju biblioteku. Pogledajte funkciju tuple() u tuple.ts. Koristeći je, možete napisati sljedeće i ne ponavljati se:
const list = tuple('a','b','c'); // type is ['a','b','c']
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'
Sretno!
ORIGINAL srpanj 2017
Jedan problem je što će literal ['a','b','c'] biti shvaćen kao tip string[], pa će sustav tipova zaboraviti posebne vrijednosti. Možete prisiliti sustav tipova da zapamti svaku vrijednost kao literalni string:
const list = ['a' as 'a','b' as 'b','c' as 'c']; // infers as ('a'|'b'|'c')[]
Ili, možda bolje, protumačite listu kao tip tuple:
const list: ['a','b','c'] = ['a','b','c']; // tuple
Ovo je iritantno ponavljanje, ali barem ne uvodi suvišan objekt tijekom izvođenja.
Sada možete dobiti svoj sindikat ovako:
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'.
Odgovor #2
Ažuriranje za TypeScript 3.4:
Nova sintaksa pod nazivom "const konteksti" koja će stići u TypeScript 3.4 omogućit će još jednostavnije rješenje koje ne zahtijeva poziv funkcije kao što je prikazano. Ova značajka je trenutno na pregledu, što se može vidjeti u ovom PR-u.
Ukratko, ova sintaksa omogućuje stvaranje nepromjenjivih nizova koji imaju uski tip (npr. tip ['a', 'b', 'c'] umjesto ('a' | 'b' | 'c')[] ili string[]). Na taj način možemo jednostavno stvarati unijske tipove iz literala kao što je prikazano u nastavku:
const MY_VALUES = <const> ['a', 'b', 'c']
type MyType = typeof MY_VALUES[number]
U alternativnoj sintaksi:
const MY_VALUES = ['a', 'b', 'c'] as const
type MyType = typeof MY_VALUES[number]
