Enums Flashcards

1
Q

What is an enum?

A

An enum (short for enumeration) is a special type of value that allows for a collection of related values with meaningful names. Enums are used to define a set of named constants that can represent a range of values, making the code more readable and expressive.

Enums in can be numeric or string-based.

// numeric
enum Direction {
    Up = 1,
    Down,
    Left,
    Right
}

// string-based
enum Color {
    Red = "RED",
    Green = "GREEN",
    Blue = "BLUE"
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

How do you define an enum?

A

An enum can be defined using the enum keyword.

enum Direction {
  Up = 1,
  Down,
  Left,
  Right,
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

What is a numeric enum?

A

A numeric enum in is a type of enum where the members have numeric values. It is used to define a set of named numeric constants, making the code more readable and expressive.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

How are values assigned to members in a numeric enum by default?

A

By default, the first member of a numeric enum is assigned the value of 0, and each subsequent member is incremented by 1.

Example:

enum Days {
    Sunday,   // 0
    Monday,   // 1
    Tuesday,  // 2
    ...
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

How can you assign custom values to members in a numeric enum?

A

You can assign custom numeric values to members of a numeric enum by explicitly setting their values. If auto-incrementing continues from the last custom value.

Example with custom values:

enum ErrorCode {
    NotFound = 404,
    InternalServerError = 500
}

Example with mixed auto-incremented and custom values:

enum Mixed {
    A = 1,
    B,       // 2
    C = 10,
    D        // 11
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

What is a string-based enum?

A

A string-based enum is a type of enum where the members have string values. It is used to define a set of named string constants, making the code more readable and expressive.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

How are values assigned to members in a string-based enum?

A

In a string-based enum, you must explicitly assign string values to each member. Unlike numeric enums, string enums do not have auto-incrementing behavior.

Example:

enum Color {
    Red = "RED",
    Green = "GREEN",
    Blue = "BLUE"
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

What are constant enum members?

A

Constant enum members are members of an enum whose values are known at compile time. They can be literal values or composed of other constant enum members.

TypeScript inlines the value of constant enums at the usage sites, which means the compiled JavaScript code doesn’t include the enum object.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

What are computed enum members?

A

Computed enum members are members of an enum whose values are computed at runtime. They are usually based on expressions or functions.

Unlike constant enum members, computed enum members require the enum object to be included in the compiled JavaScript.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

How do you define a constant enum member?

A

Constant enum members are those 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:

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

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

Example:

enum Days {
  Sunday = 0,
  Monday = 1,
  Tuesday = 2,
  // ...
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

How do you define a computed enum member?

A

Computed enum members are defined by assigning a non-constant expression to them.

Example:

enum FileAccess {
  // constant members
  None,
  Read = 1 << 1,
  Write = 1 << 2,
  ReadWrite = Read | Write,
	
  // computed member
  G = "123".length,
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

What happens when you mix constant and computed members in an enum?

A

When you mix constant and computed members in an enum, the computed members must come after the constant members. Any computed enum member that doesn’t have an initializer is considered to be constant if all preceding enum members are constant.

Example:

enum FileAccess {
  // constant members
  None,
  Read = 1 << 1,
  Write = 1 << 2,
  ReadWrite = Read | Write,
	
  // computed member
  G = "123".length,
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

What is a const enum?

A

A const enum is a special kind of enum in TypeScript. By adding the const keyword, TypeScript will inline the values at the usage sites. This has the benefit of reducing runtime overhead as the enum object is not included in the compiled JavaScript. Example:

const enum Directions {
  Up,
  Down,
  Left,
  Right
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Can you access the enum object at runtime for const enums?

A

No, const enums are completely removed during compilation and only the values are inlined where they are used. Therefore, you cannot access the enum object at runtime.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

How is the first member an enum initialized by default if it has no initializer?

A

The first member of a TypeScript enum is assigned the value 0 by default if it has no initializer.

For example:

enum E {
  X,
}

In this example, E.X is equal to 0.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

How are enum members initialized if they don’t have initializers and are not the first member?

A

If an enum member does not have an initializer and the preceding enum member was a numeric constant, the value of the current enum member will be the value of the preceding enum member plus one.

For example:

enum E1 {
  X,
  Y,
  Z,
}

In this example, E1.X is 0, E1.Y is 1, and E1.Z is 2.

17
Q

How are the values of enum members assigned if the first member has an initializer?

A

If the first member of a TypeScript enum has an initializer, the subsequent members without initializers are assigned incrementally increasing values, based on the initial value.

For example:

enum E2 {
  A = 1,
  B,
  C,
}

In this example, E2.A is 1, E2.B is 2, and E2.C is 3.

18
Q

What are literal enum members?

A

In TypeScript, literal enum members are a subset of constant enum members that aren’t calculated. A literal enum member is a constant enum member that has no initialized value or has 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).
19
Q

What happens when all members in an enum have literal enum values?

A

When all members in an enum have literal enum values in TypeScript, two special semantics come into play:

1.- Enum members themselves also become types. You can specify that certain members can only have the value of an enum member.

Example:

enum ShapeKind {
  Circle,
  Square,
}

interface Circle {
  kind: ShapeKind.Circle;
  radius: number;
}

interface Square {
  kind: ShapeKind.Square;
  sideLength: number;
}

2.- Enum types effectively become a union of each enum member. This allows the type system to leverage the fact that it knows the exact set of values that exist in the enum, enabling TypeScript to catch bugs where values might be compared incorrectly.

20
Q

Provide an example of TypeScript catching an error due to special semantics of literal enums.

A

In TypeScript, when using literal enums, the type system can catch errors where values are compared incorrectly. Here is an example:

enum E {
  Foo,
  Bar,
}

function f(x: E) {
  if (x !== E.Foo || x !== E.Bar) {
    // Error: This comparison appears to be unintentional because the types 'E.Foo' and 'E.Bar' have no overlap.
  }
}

In this example, TypeScript recognizes that the check x !== E.Foo || x !== E.Bar is likely to be unintentional because if x is not E.Foo, it can only be E.Bar, so the second part of the condition does not make sense.

21
Q

How do you get a type that represents all enum keys as strings?

A

In TypeScript, to get a type that represents all enum keys as strings, you use the keyof typeof in conjunction with the enum. This is useful because enums are real objects that exist at runtime, but the keyof keyword works differently for enums than for typical objects.

Example:

enum LogLevel {
  ERROR,
  WARN,
  INFO,
  DEBUG,
}

type LogLevelStrings = keyof typeof LogLevel;

function printImportant(key: LogLevelStrings, message: string) {
  const num = LogLevel[key];
	
  if (num <= LogLevel.WARN) {
    console.log("Log level key is:", key);
    console.log("Log level value is:", num);
    console.log("Log level message is:", message);
  }
}

printImportant("ERROR", "This is a message");

This creates a type LogLevelStrings equivalent to 'ERROR' | 'WARN' | 'INFO' | 'DEBUG'.

22
Q

What are reverse mappings enums?

A

In TypeScript, in addition to creating an object with property names for members, numeric enums also get a reverse mapping from enum values to enum names. This means that the enum object stores both forward (name -> value) and reverse (value -> name) mappings.

Example:

enum Enum {
  A,
}

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

Compiled JavaScript:

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

It’s important to note that string enum members do not get a reverse mapping generated. Only numeric enums have this behavior.

23
Q

What are ambient enums in TypeScript and how do they differ from regular enums?

A

Ambient enums in TypeScript are used to describe the shape of already existing enum types. They are declared using the declare keyword followed by enum.

Example:

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

One significant difference between ambient and non-ambient enums is in the way un-initialized members are treated. In regular enums, members that don’t have an initializer are considered constant if its preceding enum member is considered constant. However, in ambient (and non-const) enums, a member that does not have an initializer is always considered computed.

24
Q

How can an object with 'as const' be used as an alternative to an enum?

A

In modern TypeScript, an object with ‘as const’ can be used as an alternative to an enum. This method keeps your codebase aligned with the state of JavaScript, and it can be particularly useful if or when enums are added to JavaScript.

Example:

// Enum
const enum EDirection {
  Up,
  Down,
  Left,
  Right,
}

// Object as an alternative
const ODirection = {
  Up: 0,
  Down: 1,
  Left: 2,
  Right: 3,
} as const;

// Accessing values
EDirection.Up; // Enum member
ODirection.Up; // Property
25
Q

How can we use the object alternative to an enum as a parameter type in a function?

A

To use the object alternative to an enum as a parameter type in a function, we need to pull out the values with an extra line using typeof.

Example:

```
// Enum
const enum EDirection {
  Up,
  Down,
  Left,
  Right,
}

// Object as an alternative
const ODirection = {
  Up: 0,
  Down: 1,
  Left: 2,
  Right: 3,
} as const;

// Using the enum as a parameter
function walk(dir: EDirection) {}

// Pull out the values from the object
type Direction = typeof ODirection[keyof typeof ODirection];

// Use the object values as a parameter
function run(dir: Direction) {}

// Invoking the functions
walk(EDirection.Left);
run(ODirection.Right);