TypeScript tutorial: Lesson 21 – Enum


TypeScript provides both numeric and string-based enums.

Numeric enums

enum Num {
    Zero,
    One,
    Two,
    Four = 4,
    Five,
    Three = 3,
    Four_2
}

console.log(Num.Four_2) //4

Using an enum is simple: just access any member as a property off of the enum itself, and declare types using the name of the enum:

enum Votes {
    One = 1,
    Two,
    Three,
    Four,
    Five
}
  
function vote(user: number, vote: Votes): void {
    // function body
}
  
vote(1, Votes.Three)

String enums

enum Algorithms {
    CBC_128 = "CBC_128",
    ECB_128 = "ECB_128",
    CBC_64 = "CBC_64",
    ECB_64 = "ECB_64"
}

Heterogeneous enums

Technically enums can be mixed with string and numeric members, but it’s not clear why you would ever want to do so:

enum BooleanLikeHeterogeneousEnum {
  No = 0,
  Yes = "YES"
}

Computed and constant members

Each enum member has a value associated with it which can be either constant or computed. An enum member is considered constant if:

  • It is the first member in the enum and it has no initializer, in which case it’s assigned the value 0:
    // A.X is constant:
    enum A {
        X
    }
  • It does not have an initializer and the preceding enum member was a numeric constant. In this case the value of the current enum member will be the value of the preceding enum member plus one.

    // All enum members in ‘A1’ and ‘A2’ are constant.

    enum A1 {
        X,
        Y,
        Z
    }
    
    enum A2 {
        A = 1,
        B,
        C
    }
  • The enum member is initialized with a constant enum expression. A constant enum expression is a subset of TypeScript expressions that can be fully evaluated at compile time. An expression is a constant enum expression if it is:

    1. a literal enum expression (basically a string literal or a numeric literal)
    2. a reference to previously defined constant enum member (which can originate from a different enum)
    3. a parenthesized constant enum expression
    4. one of the +, -, ~ unary operators applied to constant enum expression
    5. +, -, *, /, %, <<, >>, >>>, &, |, ^ binary operators with constant enum expressions as operands

    It is a compile time error for constant enum expressions to be evaluated to NaN or Infinity.

In all other cases enum member is considered computed.

enum OtherEnums {
    One = 1
}

enum SomeEnums {
    // constant members
    None,
    Read = 1 << 1,
    Write = 1 << 2,
    ReadWrite = Read | Write, 
    Infinity = 10/0,
    Some = ~OtherEnums.One,
    // computed member
    Computed = "constant".length,        
}

console.log(SomeEnums.Some) //-2

Union enums and enum member types

There is a special subset of constant enum members that aren’t calculated: literal enum members. A literal enum member is a constant enum member with no initialized value, or with values that are initialized to

  • any string literal (e.g. “foo”, “bar, “baz”)
  • any numeric literal (e.g. 1, 100)
  • a unary minus applied to any numeric literal (e.g. -1, -100)

When all members in an enum have literal enum values, some special semantics come to play.

The first is that enum members also become types as well! For example, we can say that certain members can only have the value of an enum member:

enum ShapeKind {
    Circle,
    Square
}
  
interface Circle {
    kind: ShapeKind.Circle
    radius: number
}
  
interface Square {
    kind: ShapeKind.Square
    sideLength: number
}
  
let c:Circle = {
    //kind: ShapeKind.Square, //error: The expected type comes from property 'kind' which is declared here on type 'Circle'
    kind: ShapeKind.Circle,
    radius: 100
}

Reverse mappings

enum Enum {
    A
}
  
let a = Enum.A;
let nameOfA = Enum[a]; // "A"

After compiling

var Enum;
(function (Enum) {
    Enum[Enum["A"] = 0] = "A";
})(Enum || (Enum = {}));
var a = Enum.A;
var nameOfA = Enum[a]; // "A"

const enums

const enum Enum {
    A = 1,
    B = A * 2
}

Ambient enums

declare enum Enum {
  A = 1,
  B,
  C = 2
}

Leave a Reply