web-dev-qa-db-fra.com

Le tapuscrit a des unions, donc les énumérations sont-elles redondantes?

Depuis que TypeScript a introduit les types d'unions, je me demande s'il n'y a aucune raison de déclarer un type enum. Considérez la déclaration de type enum suivante:

enum X { A, B, C }
var x:X = X.A;

et une déclaration de type d'union similaire:

type X: "A" | "B" | "C"
var x:X = "A";

S'ils servent essentiellement le même objectif et que les syndicats sont plus puissants et expressifs, pourquoi les énumérations sont-elles nécessaires?

33
prmph

Pour autant que je vois, ils ne sont pas redondants, pour la raison très simple que les types d'union sont purement un concept de temps de compilation alors que les énumérations sont réellement transpilées et se retrouvent dans le javascript résultant ( exemple ).

Cela vous permet de faire certaines choses avec des énumérations, qui sont autrement impossibles avec les types d'union (comme énumération des valeurs d'énumération possibles )

23
Amid

Il y a quelques raisons pour lesquelles vous voudrez peut-être utiliser un enum

Je vois les grands avantages de l'utilisation d'une union, c'est qu'ils fournissent un moyen succinct de représenter une valeur avec plusieurs types et ils sont très lisibles. let x: number | string

EDIT: à partir de TypeScript 2.4, les énumérations prennent désormais en charge les chaînes.

enum Colors {
  Red = "RED",
  Green = "GREEN",
  Blue = "BLUE",
} 
15
matthew

Les énumérations peuvent être considérées conceptuellement comme un sous-ensemble de types d'union, dédié aux valeurs int et/ou string, avec quelques fonctionnalités supplémentaires mentionnées dans d'autres réponses qui les rendent conviviales à utiliser.

Mais les énumérations ne sont pas totalement sûres:

enum Colors { Red, Green, Blue }
const c: Colors = 100; // No errors!

type Color =
    | 0 | 'Red'
    | 1 | 'Green'
    | 2 | 'Blue';

const c2: Color = 100; // Error: Type '100' is not assignable to type 'Color'

Les types d'union prennent en charge des données et des structures hétérogènes, permettant le polymorphisme par exemple:

class RGB {
    constructor(
        readonly r: number,
        readonly g: number,
        readonly b: number) { }

    toHSL() {
        return new HSL(0, 0, 0); // Fake formula
    }
}

class HSL {
    constructor(
        readonly h: number,
        readonly s: number,
        readonly l: number) { }

    lighten() {
        return new HSL(this.h, this.s, this.l + 10);
    }
}

function lightenColor(c: RGB | HSL) {
    return (c instanceof RGB ? c.toHSL() : c).lighten();
}

Entre les énumérations et les types d'union, les singletons peuvent remplacer les énumérations. C'est plus verbeux mais aussi plus orienté objet :

class Color {
    static readonly Red   = new Color(1, 'Red',   '#FF0000');
    static readonly Green = new Color(2, 'Green', '#00FF00');
    static readonly Blue  = new Color(3, 'Blue',  '#0000FF');

    static readonly All: ReadonlyArray<Color> = [
        Color.Red,
        Color.Green,
        Color.Blue,
    ]; // `[...] as readonly` to infer `ReadonlyArray<Color>` in TypeScript 3.4

    private constructor(
        readonly id: number,
        readonly label: string,
        readonly hex: string) { }
}

const c = Color.Red;

const colorIds = Color.All.map(x => x.id);

J'ai tendance à regarder F # pour voir les bonnes pratiques de modélisation. Une citation d'un article sur F # énumère sur F # pour le plaisir et le profit qui peut être utile ici:

En général, vous devriez préférer les types d'union discriminés aux énumérations, sauf si vous avez vraiment besoin d'avoir un int (ou un string) valeur qui leur est associée

3
Romain Deneau