Typescript has unions, so are enums redundant?

China☆狼群 提交于 2019-12-03 22:04:02
Amid

As far as I see they are not redundant, due to the very simple reason that union types are purely a compile time concept whereas enums are actually transpiled and end up in the resulting javascript (sample).

This allows you to do some things with enums, that are otherwise impossible with union types (like enumerating the possible enum values)

There are few reasons you might want to use an enum

I see the big advantages of using a union is that they provide a succinct way to represent a value with multiple types and they are very readable. let x: number | string

EDIT: As of TypeScript 2.4 Enums now support strings.

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

Enums can be seen conceptually as a subset of union types, dedicated to int and/or string values, with a few additional features mentioned in other responses that make them friendly to use.

But enums are not totally safe:

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'

Union types support heterogenous data and structures, enabling polymorphism for instance:

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();
}

In between enums and union types, singletons can replace enums. It's more verbose but also more object-oriented:

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);

I tend to look at F# to see good modeling practices. A quote from an article on F# enums on F# for fun and profit that can be useful here:

In general, you should prefer discriminated union types over enums, unless you really need to have an int (or a string) value associated with them

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!