All about Enum in Typescript
Tags: typescript, javascript, webdev, programming
Published: 2022-11-15 15:51:10

Enums are used to define named constants in Typescript. It's the typescripts own feature which is not a type level extension of javascript. It is defined using `enum` keyword.

Typescript has three types of enums:

Namely,

1. Numeric Enums
2. String Enums
3. Heterogeneous Enums

## Numeric Enums.
```typescript
enum Numeric {
    First,
    Second,
    Third,
    Fourth
}
console.log(Numeric.Second); // 1
```

1. A numeric enum's first member is not initialized then it starts from 0 and `Second` becomes 1.

```typescript
enum Numeric {
    First = 1,
    Second,
    Third,
    Fourth
}
console.log(Numeric.Second); // 2
```

2. Numeric enum is now initialized with 1 hence starts from 1 and `Second` becomes 2.

## String Enums

An enum which is initialized with string literal is called String enum.
```typescript
enum StringEnum {
    A = "a",
    B = "b",
    C = "c",
    D = "d",
}
```

## Heterogeneous enums

Mix of numeric and string enums.
```typescript
enum HeterogeneousEnum {
    No = 0,
    Yes = 'YES'
}
```
3. Duplicate Identifier is not allowed in Enum.

## Using an enum

i. Access any member as the property of enum itself.
```typescript
console.log(Numeric.Second); // 1
```
ii. Declare types using the name of the enum.
```typescript
(function myFunc(word, number: Numeric) {
    console.log(word + ' ' + direction);
})('word', Numeric.Second);
// word 1
```
4. Enum without initializers need to be first.
```typescript
function getConstantValue() {
    return 1;
}

enum Numeric {
    first,
    second = getConstantValue(),
}
// first & second shouldn't be upside down.
```

5. Enums without initializers should come after initialized enums with numeric constants or other enum constants.like below:
```typescript
enum Numeric {
    first = 2,
    second = A,
    third
}
```

But not like below.
```typescript
enum Numeric {
     first = 2,
     second = getConstantValue(), 
     third
 }
```


6. Each enum member has a value associated with it which can be either constant or computed.
```typescript
enum constNComputeEnum {
    first = 1,
    second = first, // reference to previously defined constant enum member.
    third = (1 + 2), // parenthesized constant enum expression,
    fourth = +true, // Unary operator applied.
    fifth = 1 & 2 // binary operator applied.
}
```

7. In All other cases enum member considered computed.

8. Computed values are not permitted in an enum with string valued members.
like below:
```typescript
enum constNComputeEnum {
     first = 1,
     second = 'second',
     third = constNComputeEnum.first, // not allowed
 }
```

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

`Ideally, below is not accepted but somehow not throwing any error.` I checked with latest typescript and is not throwing any error.
```typescript
enum NaNNInfinity {
    first = 0 / 0, // Result in NaN
    second = 1 / 0, // Result in Infinity
}
```

## Reverse Mapping
Numeric enum members can get a reverse mapping from enum values to enum names.
```typescript
enum reverseMappingEnum {
    first
}

const firstItem = reverseMappingEnum.first;
console.log(reverseMappingEnum[firstItem]); // first
```

## const enums.
To avoid generating extra js code we can declare any enum as const. These are completely removed after compilation and can't have computed members.
It is recommended to not use const enums at all.
```typescript
const enum constEnum {
    first
}
```
Compile above and check its corresponding js file and you find nothing.

## Ambient Enums

*Regular Enums*: Member that doesn't have initializer will be considered const if its preceding enum member considered const. 
*Ambient Enums*: Member that doesn't have initializer will be considered as computed.
```typescript
enum AmbientEnum {
    first = 1,
    second,
    third = 2,
}
```

## Object vs Enums
Typescript Enum exactly equal to object with as const.
```typescript
const enum Numeric {
    first,
    second,
    third,
    fourth,
  }
   
  const Numeric = {
    first: 0,
    second: 1,
    third: 2,
    fourth: 3,
  } as const;
```

## Literal Enum
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. "first", "second")
- any numeric literal (e.g. 1 to 100)
- a unary minus applied to any numeric literal (e.g. -2, -4)

Enum members also become types as well!
```typescript
enum Numeric {
    thirty,
    forty,
  }
   
  interface Person {
    age: Numeric.thirty;
    id: number
  }
   
  let c: thirty = {
    age: Numeric.forty,
    id: 100,
  };
```
Shows compilation error as: `Type 'Numeric.forty' is not assignable to type 'Numeric.thirty'.`

Enum types themselves effectively become a union of each enum member.
```typescript 
  enum Numeric {
    first,
    second,
  }
   
  function myFunc(type: Numeric) {
    if (type !== Numeric.first || type !== Numeric.second) {
    }
  }
```
Shows compilation error as: `This condition will always return 'true' since the types 'Numeric.first' and 'Numeric.second' have no overlap.`