> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Union Types and Enums

> Explains TypeScript union literal types and enums, comparing use cases and trade offs to improve type safety and prevent invalid values with duck examples.

In this lesson we'll explore how union literal types and enums in TypeScript improve type safety and when you should prefer one over the other. These features help prevent invalid values at compile time and make your domain models clearer to consumers of your types.

## The problem: using plain `string` allows invalid values

If a property is typed as `string`, any string is allowed — including nonsensical values such as `"Banana"`. This can lead to bugs only caught at runtime.

```typescript theme={null}
class PondDuck {
  name: string;
  age: number;
  type: string;
  color: string; // problem: any string allowed
  isFlying: boolean;

  constructor(name: string, age: number, type: string, color: string) {
    this.name = name;
    this.age = age;
    this.type = type;
    this.color = color;
    this.isFlying = false;
  }

  fly(): void {
    if (!this.isFlying) {
      this.isFlying = true;
      console.log(`${this.name} starts flying!`);
    } else {
      console.log(`${this.name} is already flying!`);
    }
  }

  land(): void {
    if (this.isFlying) {
      this.isFlying = false;
      console.log(`${this.name} lands gracefully`);
    } else {
      console.log(`${this.name} is already on the ground!`);
    }
  }
}

const daffy = new PondDuck('Daffy', 3, 'Mallard', 'Banana'); // allowed when color is `string`
daffy.fly();
```

## Union literal types: restrict a value to a fixed set of literals

A union of literal types restricts a variable to one of several specified literal values. This keeps invalid values out at compile time while remaining lightweight (no runtime object is created).

```typescript theme={null}
type DuckColor = 'White' | 'Brown' | 'Black' | 'Mixed';

// This will be a compile-time error:
// const myColor: DuckColor = 'Banana'; // Error: Type '"Banana"' is not assignable to type 'DuckColor'.

// You can add other allowed literals:
const myColor: DuckColor = 'Black';
```

Notes about literal unions:

* Each union member is a literal type (for example the type `"White"`, not `string`).
* Literal unions can combine primitives and more complex literal values:

```typescript theme={null}
type WeirdColor = 'White' | 3 | { shade: 'dark'; color: 'Green' };

const a: WeirdColor = 'White';
const b: WeirdColor = 3;
const c: WeirdColor = { shade: 'dark', color: 'Green' };
```

## When to use an enum

Enums group related named constants. They are useful when you want a named collection that you can reference across your codebase and benefit from editor completions. Enums also produce a runtime object (which can be numeric or string-valued).

Numeric enum example:

```typescript theme={null}
enum NumericDuckType {
  Mallard,  // 0
  Muscovy,  // 1
  Pekin     // 2
}

console.log(NumericDuckType.Mallard); // 0
```

String enum example:

```typescript theme={null}
enum DuckType {
  Mallard = 'Mallard',
  Muscovy = 'Muscovy',
  Pekin = 'Pekin',
}

console.log(DuckType.Mallard); // 'Mallard'
```

<Callout icon="lightbulb" color="#1CB2FE">
  Use union literal types when you want a compact set of allowed literals (strings/numbers) without an extra runtime object. Use enums when you want a named collection you can reference (with code completion) and potentially map to numeric or string values at runtime.
</Callout>

## Putting it together: a typed Duck class

Replace unconstrained `string` types with `DuckColor` (a union of literals) and `DuckType` (an enum). The compiler now prevents invalid values and provides better tooling support.

```typescript theme={null}
type DuckColor = 'White' | 'Brown' | 'Black' | 'Mixed';

enum DuckType {
  Mallard = 'Mallard',
  Muscovy = 'Muscovy',
  Pekin = 'Pekin',
}

class PondDuck {
  name: string;
  age: number;
  type: DuckType;
  color: DuckColor;
  isFlying: boolean;
  favoriteFood?: string;

  constructor(
    name: string,
    age: number,
    type: DuckType,
    color: DuckColor,
    favoriteFood?: string
  ) {
    this.name = name;
    this.age = age;
    this.type = type;
    this.color = color;
    this.isFlying = false;
    this.favoriteFood = favoriteFood;
  }

  quack(times = 1): void {
    for (let i = 0; i < times; i++) {
      console.log(`${this.name} the ${this.color} ${this.type} duck says: Quack!`);
    }
  }

  fly(): void {
    if (!this.isFlying) {
      this.isFlying = true;
      console.log(`${this.name} starts flying!`);
    } else {
      console.log(`${this.name} is already flying!`);
    }
  }

  land(): void {
    if (this.isFlying) {
      this.isFlying = false;
      console.log(`${this.name} lands gracefully!`);
    } else {
      console.log(`${this.name} is already on the ground!`);
    }
  }
}

// Correct usage:
const daffy = new PondDuck('Daffy', 3, DuckType.Mallard, 'Black', 'Corn');
const donald = new PondDuck('Donald', 5, DuckType.Pekin, 'White');

daffy.fly();
daffy.fly();
daffy.land();
daffy.land();
donald.fly();

// Invalid usage (will be a TypeScript compile error):
// const bad = new PondDuck('Elmer', 2, DuckType.Mallard, 'Banana');
// Error: Type '"Banana"' is not assignable to parameter of type 'DuckColor'.
```

Sample runtime output (when valid values are used):

```text theme={null}
Daffy starts flying!
Daffy is already flying!
Daffy lands gracefully!
Daffy is already on the ground!
Donald starts flying!
```

## Quick comparison: union literal types vs enums

| Feature           | Union literal types                                      | Enums                                                   |                                         |
| ----------------- | -------------------------------------------------------- | ------------------------------------------------------- | --------------------------------------- |
| Runtime footprint | None (compile-time only)                                 | Creates a runtime object                                |                                         |
| Best for          | Small sets of primitives (strings/numbers)               | Named collections, mapping to runtime values            |                                         |
| Tooling           | Type safety, autocomplete on variables typed with unions | Strong autocomplete for enum members and runtime access |                                         |
| Example           | \`type DuckColor = 'White'                               | 'Brown'\`                                               | `enum DuckType { Mallard = 'Mallard' }` |

## Summary

* Use union literal types to restrict values to a compact set of allowed literals at the type level.
* Use enums when you want a named runtime object (numeric or string) and stable identifiers across your code.
* Applying these TypeScript features makes your APIs and domain models safer, more self-documenting, and easier to use.

## Links and references

* [TypeScript Handbook: Union Types](https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html)
* [TypeScript Handbook: Enums](https://www.typescriptlang.org/docs/handbook/enums.html)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/cdk-for-terraform-with-typescript/module/eb523de4-1aeb-429a-820a-20d9f6426562/lesson/2c9dcf6b-2550-4677-90c2-062589ed9929" />
</CardGroup>
