167 votes

Typescript dériver le type d'union à partir des valeurs de tuple / array

Dites que j'ai la liste const list = ['a', 'b', 'c']

Est-il possible de déduire de ce type d'union valeur 'a' | 'b' | 'c' ?

Je veux cela parce que je veux définir un type qui n'autorise que les valeurs d'un tableau statique, et que je dois également énumérer ces valeurs à l'exécution, aussi j'utilise tableau.

Exemple de mise en œuvre avec un objet indexé:

 const indexed = {a: null, b: null, c: null}
const list = Object.keys(index)
type NeededUnionType = keyof typeof indexed
 

Je me demande s'il est possible de le faire sans utiliser une carte indexée.

367voto

jcalz Points 30410

Mise à JOUR Février 2019

Dans Tapuscrit 3.4, qui devrait être publié en Mars 2019 , il sera possible de dire au compilateur de déduire le type d'un n-uplet de littéraux comme un n-uplet de littéraux, au lieu de, disons, string[], à l'aide de la as const de la syntaxe. Ce type d'affirmation, le compilateur en déduire le plus étroit possible pour une valeur, y compris de faire tout readonly. Il devrait ressembler à ceci:

const list = ['a', 'b', 'c'] as const; // TS3.4 syntax
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c';

Cela permettra d'éviter le recours à une fonction d'assistance de toute nature. Bonne chance à tous!


Mise à JOUR en juillet 2018

Il ressemble, en commençant par Tapuscrit 3.0, il sera possible pour le Tapuscrit pour automatiquement déduire tuple types. Une fois relâchée, tuple() de la fonction dont vous avez besoin peut être succinctement écrite comme suit:

export type Lit = string | number | boolean | undefined | null | void | {};
export const tuple = <T extends Lit[]>(...args: T) => args;

Et puis vous pouvez l'utiliser comme ceci:

const list = tuple('a','b','c');  // type is ['a','b','c']
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'

Espérons que fonctionne pour les gens!


Mise à JOUR décembre 2017

Depuis que j'ai posté cette réponse, j'ai trouvé un moyen de déduire tuple types si vous êtes prêt à ajouter une fonction à votre bibliothèque. Découvrez la fonction tuple() en n-uplet.ts. En l'utilisant, vous êtes en mesure d'écrire la suite et de ne pas répéter vous-même:

const list = tuple('a','b','c');  // type is ['a','b','c']
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'

Bonne chance!


ORIGINAL juillet 2017

Un problème est que les littérale ['a','b','c'] sera déduit comme étant de type string[], de sorte que le type de système d'oublier les valeurs spécifiques. Vous pouvez forcer le type de système à se souvenir de chaque valeur comme une chaîne littérale:

const list = ['a' as 'a','b' as 'b','c' as 'c']; // infers as ('a'|'b'|'c')[]

Ou, peut-être mieux, d'interpréter la liste comme un tuple de type:

const list: ['a','b','c'] = ['a','b','c']; // tuple

Ce qui est gênant pour la répétition, mais au moins il n'a pas d'introduire un parasite de l'objet au moment de l'exécution.

Vous pouvez maintenant obtenir votre union comme ceci:

type NeededUnionType = typeof list[number];  // 'a'|'b'|'c'.

Espérons que cela aide.

25voto

ggradnig Points 2763

Mise à jour pour le Tapuscrit 3.4:

Une nouvelle syntaxe appelé "const contextes" , qui arrivera en caractères d'imprimerie 3.4 permettra une même solution, plus simple, qui ne nécessite pas d'appel de fonction, comme l'a démontré. Cette fonctionnalité est actuellement en examen dans cette PR.

En bref, cette syntaxe permet la création de immuable des tableaux qui ont une vision étroite de type (c'est à dire le type d' ['a', 'b', 'c'] au lieu de ('a' | 'b' | 'c')[] ou string[]). De cette façon, nous pouvons créer de l'union types de littéraux simples comme indiqué ci-dessous:

const MY_VALUES = <const> ['a', 'b', 'c']
type MyType = typeof MY_VALUES[number]

Dans la variante de la syntaxe:

const MY_VALUES = ['a', 'b', 'c'] as const
type MyType = typeof MY_VALUES[number]

3voto

unional Points 4754

Il n'est pas possible de faire cela avec Array.

La raison en est que, même si vous déclarez la variable en tant que const, le contenu d'un tableau peut toujours changer, ainsi @jonrsharpe mentionne qu'il s'agit de l'exécution.

Étant donné ce que vous voulez, il peut être préférable d’utiliser interface avec keyof :

 interface X {
    a: string,
    b: string
}

type Y = keyof X  // Y: 'a' | 'b'
 

Ou enum :

 enum X { a, b, c }
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X