//

TypeScript Enums vs String Literals

Enums help to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases.

Enums are one of the few features TypeScript has which is not a type-level extension of JavaScript.

Enums are one of the few features TypeScript has which is not a type-level extension of JavaScript.

TypeScript 2.4 implemented one of the most requested features: string enums, or, to be more precise, enums with string-valued members.

Prefer union types over string enums

String enums are named types, That means, even if values are valid and compatible, you can’t pass them to a function or object where you expect a string enum. See this example

enum User {
  Admin = 'Admin',
  Client = 'Client',
}

function login(user: User) {
  API.login();
}

login('Admin'); // This is not allowed!
login(User.Admin); // You have to be explicit!

Using union type gives you something that works similarly and is much more aligned with TypeScript.

type User = 'Admin' | 'Client';

function login(user: User) {
  API.login();
}

login('Admin');

The benefits of using a string literal are:

  • The transpiled code is much smaller compared to an enum (see below)
  • The same benefits you get from enums are valid for string literals: Type-safety, auto-complete, predictable behaviour
  • You don’t have to export the enum declaration if it is used in other files.

The code emitted by the TypeScript compiler for an enum:

'use strict';
var User;
(function (User) {
  User['Admin'] = 'Admin';
  User['Client'] = 'Client';
})(User || (User = {}));

Object const for better naming

If you still want to write your code enum-style, with an object and a named identifier, a const object might just give you the desired behaviour and is much closer to JavaScript.

If the values are harder to read or makes no sense, you can use const objects to give meaningful references.

const User {
  ADMIN: "Admin",
  CLIENT: "Client",
} as const;

function login(user: User) {
  API.login()
};

login(User.Admin);

When to use enums

For scenarios where a reference makes sense rather than a value, and it’s used for conditional logic, an enum can be used.

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

/**
 * This is equivalent to:
 * type LogLevelStrings = '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');

Summary

  1. Use string literal over string enums all the times
  2. If the values in string literals are harder to read or makes no sense, you can use const objects to give meaningful references.
  3. For scenarios where a reference makes sense rather than a value, and it’s used for conditional logic, an enum can be used.