← Back to Course

🎨 Enums & Literal Types

Enums and literal types help you define sets of named constants and specific allowed values. The TypeLand Adventure Game uses enums extensively for movement directions, terrain types, and collectible kinds! Let's explore how they work.

Numeric Enums

Numeric enums auto-increment from an initial value (default: 0):

Example: Numeric Enums

// Basic numeric enum (starts at 0)
enum Direction {
  North,    // 0
  East,     // 1
  South,    // 2
  West      // 3
}

const facingDirection: Direction = Direction.North;
console.log(facingDirection);  // 0

// Custom starting value
enum StatusCode {
  OK = 200,
  BadRequest = 400,
  Unauthorized = 401,
  NotFound = 404,
  ServerError = 500
}

function handleResponse(code: StatusCode): string {
  switch (code) {
    case StatusCode.OK:
      return "Success!";
    case StatusCode.NotFound:
      return "Resource not found";
    default:
      return "Error occurred";
  }
}

String Enums

String enums provide more readable values and better debugging:

Example: String Enums

// String enum (TypeLand uses these!)
enum MovementKey {
  NORTH = "NORTH",
  SOUTH = "SOUTH",
  EAST = "EAST",
  WEST = "WEST"
}

enum GroundMaterial {
  MEADOW = "MEADOW",
  STONE = "STONE",
  LAKE = "LAKE",
  SAND = "SAND"
}

enum CollectibleKind {
  TREASURE_CHEST = "TREASURE_CHEST",
  MAGIC_ORB = "MAGIC_ORB",
  ANCIENT_SCROLL = "ANCIENT_SCROLL",
  CRYSTAL_GEM = "CRYSTAL_GEM"
}

// Using string enums
function canWalkOn(terrain: GroundMaterial): boolean {
  return terrain === GroundMaterial.MEADOW || 
         terrain === GroundMaterial.SAND;
}

console.log(canWalkOn(GroundMaterial.MEADOW));  // true
console.log(canWalkOn(GroundMaterial.LAKE));    // false

Const Enums

Const enums are removed during compilation for better performance:

Example: Const Enums

// Const enum - inlined at compile time
const enum Color {
  Red = "#FF0000",
  Green = "#00FF00",
  Blue = "#0000FF"
}

// This code:
const bgColor = Color.Red;
const textColor = Color.Blue;

// Compiles to (no enum object created):
const bgColor = "#FF0000";
const textColor = "#0000FF";

// Const enums reduce bundle size
const enum LogLevel {
  Debug,
  Info,
  Warning,
  Error
}

String Literal Types

String literal types define exact values a variable can have:

Example: String Literal Types

// Union of string literals
type Difficulty = "easy" | "normal" | "hard" | "expert";

function setGameDifficulty(level: Difficulty): void {
  console.log(`Difficulty set to: ${level}`);
}

setGameDifficulty("normal");  // ✓ OK
// setGameDifficulty("medium");  // ✗ Error - not in the union

// Combine with type aliases
type Alignment = "left" | "center" | "right";
type VerticalAlign = "top" | "middle" | "bottom";

function positionElement(
  horizontal: Alignment,
  vertical: VerticalAlign
): void {
  // Position logic
}

positionElement("center", "middle");

Numeric Literal Types

Literal types also work with numbers and booleans:

Example: Numeric Literal Types

// Specific number values
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
type BinaryDigit = 0 | 1;

function rollDice(): DiceRoll {
  return (Math.floor(Math.random() * 6) + 1) as DiceRoll;
}

// Boolean literals
type Yes = true;
type No = false;

// Mixed literal type
type Port = 3000 | 4000 | 8080 | "auto";

function startServer(port: Port): void {
  console.log(`Server starting on port: ${port}`);
}

startServer(3000);    // ✓ OK
startServer("auto");  // ✓ OK
// startServer(5000);  // ✗ Error

Enums vs Literal Types

When should you use each?

Comparison: Enums vs Literals

// Enum approach
enum HttpMethod {
  GET = "GET",
  POST = "POST",
  PUT = "PUT",
  DELETE = "DELETE"
}

function request(method: HttpMethod, url: string): void {
  // Implementation
}

request(HttpMethod.GET, "/api/users");

// Literal type approach
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";

function request(method: HttpMethod, url: string): void {
  // Implementation
}

request("GET", "/api/users");

// When to use enums:
// - Need reverse mapping (value to name)
// - Want a runtime object with all values
// - Building a library/framework
// - Want auto-completion without quotes

// When to use literal types:
// - Simpler, more lightweight
// - Better tree-shaking
// - More flexible (can be extended easily)
// - Modern TypeScript recommendation

Real Example from TypeLand

Example: TypeLand Adventure Game

// From TypeLand's definitions.ts

// Movement directions enum
enum MovementKey {
  NORTH = "NORTH",
  SOUTH = "SOUTH",
  EAST = "EAST",
  WEST = "WEST"
}

// Terrain types enum
enum GroundMaterial {
  MEADOW = "MEADOW",
  STONE = "STONE",
  LAKE = "LAKE",
  SAND = "SAND"
}

// Collectible types enum
enum CollectibleKind {
  TREASURE_CHEST = "TREASURE_CHEST",
  MAGIC_ORB = "MAGIC_ORB",
  ANCIENT_SCROLL = "ANCIENT_SCROLL",
  CRYSTAL_GEM = "CRYSTAL_GEM"
}

// Using enums in the game engine
class AdventureEngine {
  private movementDeltas = new Map<MovementKey, [number, number]>([
    [MovementKey.NORTH, [-1, 0]],
    [MovementKey.SOUTH, [1, 0]],
    [MovementKey.EAST, [0, 1]],
    [MovementKey.WEST, [0, -1]]
  ]);
  
  canMove(terrain: GroundMaterial): boolean {
    return terrain === GroundMaterial.MEADOW || 
           terrain === GroundMaterial.SAND;
  }
}

🎯 Key Concepts

  • Enums create named constants with better type safety
  • String enums are more readable and easier to debug
  • Const enums are inlined for better performance
  • Literal types define exact allowed values
  • Union of literals: "a" | "b" | "c"
  • Modern TypeScript favors literal types over enums for simplicity
  • TypeLand Adventure uses enums for game constants!
← Back to Intermediate Course