web-dev-qa-db-fra.com

Énumérer comme paramètre dans le script

N'est-il pas possible de définir le type d'un paramètre sur Enum? Comme ça:

private getRandomElementOfEnum(e : enum):string{
    var length:number = Object.keys(e).length;
    return e[Math.floor((Math.random() * length)+1)];
}

Si je fais cela, Intellij marque ce code comme inconnu. Et suggérer de renommer la variable, est-ce que cela a du sens?

private getRandomElementOfEnum(e : any):string{
    var length:number = Object.keys(e).length;
    return e[Math.floor((Math.random() * length)+1)];
}

Ce code fonctionne bien. mais n'est pas aussi élégant et conventuel.

Existe-t-il une possibilité ou une petite solution de contournement pour définir une énumération comme paramètre?

MODIFIER:

Après avoir étudié ces réponses, puis-je le faire également avec un ensemble d'enum défini, sth similaire à enum1 | enum2 | enum3?

31
Synoon

Il n'est pas possible de garantir que le paramètre est une énumération, car les énumérations dans TS n'héritent pas d'un ancêtre ou d'une interface commune.

TypeScript apporte une analyse statique. Votre code utilise une programmation dynamique avec Object.keys Et e[dynamicKey]. Pour les codes dynamiques, le type any est pratique.

Votre code est bogué: length() n'existe pas, e[Math.floor((Math.random() * length)+1)] renvoie une chaîne ou un entier, et les valeurs d'énumération peuvent être définies manuellement

Voici une suggestion:

function getRandomElementOfEnum<E>(e: any): E {
    var keys = Object.keys(e),
        index = Math.floor(Math.random() * keys.length),
        k = keys[index];
    if (typeof e[k] === 'number')
        return <any>e[k];
    return <any>parseInt(k, 10);
}

function display(a: Color) {
    console.log(a);
}

enum Color { Blue, Green };
display(getRandomElementOfEnum<Color>(Color));

Idéalement, le type de paramètre any devrait être remplacé par typeof E Mais le compilateur (TS 1.5) ne peut pas comprendre cette syntaxe.

15
Paleo

Vous pouvez faire mieux que any:

enum E1 {
    A, B, C
}
enum E2 {
    X, Y, Z
}

function getRandomElementOfEnum(e: { [s: number]: string }): number {
    /* insert working implementation here */
    return undefined;
}

// OK
var x: E1 = getRandomElementOfEnum(E1);
// Error
var y: E2 = getRandomElementOfEnum(window);
// Error
var z: string = getRandomElementOfEnum(E2);
15
Ryan Cavanaugh

Je suis d'accord avec @Tarh. Les énumérations dans TypeScript ne sont que des objets Javascript sans interface ni prototype communs (et s'ils sont const enum, alors ce ne sont même pas des objets), vous ne pouvez donc pas restreindre les types à "n'importe quelle énumération".

Le plus proche que je pourrais obtenir est quelque chose comme ceci:

enum E1 {
    A, B, C
}
enum E2 {
    X, Y, Z
}

// make up your own interface to match TypeScript enums
// as closely as possible (not perfect, though)
interface Enum {
    [id: number]: string
}

function getRandomElementOfEnum(e: Enum): string {
   let length = Object.keys(e).length / 2;
   return e[Math.floor((Math.random() * length))];
}

Cela fonctionne pour toutes les énumérations (sans les initialiseurs personnalisés), mais il accepterait également d'autres tableaux en entrée (puis échouerait car le corps de la méthode repose sur la structure de clé très spécifique des énumérations TypeScript).

Donc, à moins que vous n'ayez vraiment besoin d'une telle fonction "générique", créez des fonctions de type sécurisées pour les types d'énumération individuels (ou un type d'union comme E1|E2|E3) dont vous avez réellement besoin.

Et si vous avez ce besoin (et cela pourrait très bien être un problème X-Y qui peut être résolu de manière meilleure et complètement différente dans un contexte plus large), utilisez any, car vous avez de toute façon quitté le territoire de typesafe.

8
Thilo

Une autre option possible non mentionnée ci-dessus consiste à utiliser les valeurs réelles. Ceci n'est cependant possible que lorsque vous connaissez toutes les options. À mon avis, c'est certainement mieux que tout.

    doSomething(a: string, b: 'this'|'can'|'work'): void {
     //do something
    }
2
selinathat

Résumant les réponses précédentes avec un peu de nouvelle syntaxe - une fonction générique typesafe, qui fonctionne avec des énumérations numériques ainsi que des énumérations de chaînes:

function getRandomElementOfEnum<T extends {[key: number]: string | number}>(e: T): T[keyof T] {
  const keys = Object.keys(e);

  const randomKeyIndex = Math.floor(Math.random() * keys.length);
  const randomKey = keys[randomKeyIndex];

  // Numeric enums members also get a reverse mapping from enum values to enum names.
  // So, if a key is a number, actually it's a value of a numeric enum.
  // see https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings
  const randomKeyNumber = Number(randomKey);
  return isNaN(randomKeyNumber)
    ? e[randomKey as keyof T]
    : randomKeyNumber as unknown as T[keyof T];
}
2
Valeriy Katkov

La solution de @ selinathat n'est excellente que si vous avez peu de types. mais si nous en avons plus? par exemple :

doSomething(a: string, b: 'this'|'can'|'work'|'test1'|'test2'|'test3'): void {
 //do something
}

son assez laid hah!? je préfère utiliser keyof:

interface Items {
    'this',
    'can',
    'work',
    'test1',
    'test2',
    'test3',
}

doSomething(a: string, b: keyof Items): void {
 //do something
}
1
MajiD