Dis que j'ai une liste const list = ['a', 'b', 'c']

Est-il possible de dériver de ce type d'union de valeurs qui est 'a' | 'b' | 'c'?

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

Exemple comment il peut être implémenté 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.

98
WHITECOLOR 22 juil. 2017 à 09:54

2 réponses

Meilleure réponse

MISE À JOUR Fév 2019

Dans TypeScript 3.4, qui devrait être publié en mars 2019, il sera être possible de dire au compilateur d'inférer le type d'un tuple de littéraux comme un tuple de littéraux , au lieu de, disons, string[], en utilisant le as const syntaxe. Ce type d'assertion amène le compilateur à déduire le type le plus étroit possible pour une valeur, y compris faire tout readonly. Ça devrait ressembler à ça:

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

Cela évitera le besoin d'une fonction d'assistance de quelque nature que ce soit. Bonne chance à tous!


MISE À JOUR juillet 2018

Il semble qu'à partir de TypeScript 3.0, il sera possible pour TypeScript de déduire automatiquement des types de tuple . Une fois publiée, la fonction tuple() dont vous avez besoin peut être écrite succinctement 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'

J'espère que cela fonctionne pour les gens!


MISE À JOUR décembre 2017

Depuis que j'ai publié cette réponse, j'ai trouvé un moyen de déduire des types de tuple si vous êtes prêt à ajouter une fonction à votre bibliothèque. Découvrez la fonction tuple() dans tuple.ts. En l'utilisant, vous êtes capable d'écrire ce qui suit et de ne pas vous répéter:

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 le littéral ['a','b','c'] sera déduit comme type string[], donc le système de types oubliera les valeurs spécifiques. Vous pouvez forcer le système de types à mémoriser chaque valeur sous forme de chaîne littérale:

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

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

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

C'est une répétition ennuyeuse, mais au moins, cela n'introduit pas d'objet étranger au moment de l'exécution.

Maintenant, vous pouvez obtenir votre syndicat comme ceci:

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

J'espère que cela pourra aider.

177
jcalz 25 avril 2019 à 13:32

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

La raison est que, même si vous déclarez la variable comme const, le contenu d'un tableau peut encore changer, donc @jonrsharpe mentionne qu'il s'agit du runtime.

Compte tenu de 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 }
2
unional 22 juil. 2017 à 07:58