Back to articles

TypeScript Interface, Enum, and Type: Roles and Differences

15 min
Front-endTypeScript

TypeScript Interface, Enum, and Type: Roles and Differences

TypeScript gives you three main tools for defining types: interface, enum, and type.

All three describe data shapes or type constraints, but they serve different purposes and behave differently in important ways.


Interface

interface describes the shape of an object — what properties and methods it should have.

TypeScript
interface User {
  id: number;
  name: string;
  email: string;
}

const user: User = {
  id: 1,
  name: 'Charmy',
  email: 'charmy@example.com',
};

Optional and Readonly Properties

TypeScript
interface User {
  id: number;
  name: string;
  email?: string;            // optional
  readonly createdAt: Date;  // can't be reassigned after initialization
}

Extending

Interfaces can extend other interfaces to build up more complex shapes:

TypeScript
interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

const dog: Dog = {
  name: 'Max',
  breed: 'Labrador',
};

Multiple inheritance works too:

TypeScript
interface C extends A, B { ... }

Declaration Merging

Declaring the same interface name twice merges them — this is unique to interface:

TypeScript
interface User {
  name: string;
}

interface User {
  age: number;
}

// User now has both name and age
const user: User = { name: 'Charmy', age: 25 };

This is particularly useful for augmenting types from third-party libraries.


Type

type creates a type alias — a name you can give to any type, not just objects.

TypeScript
type UserId = number;
type UserName = string;

type User = {
  id: UserId;
  name: UserName;
};

Union Types

TypeScript
type Status = 'active' | 'inactive' | 'pending';
type ID = number | string;

function getUser(id: ID): User { ... }

Intersection Types

TypeScript
type Admin = User & { role: 'admin' };

Combines all properties from multiple types into one.

Utility Types

type works naturally with TypeScript's built-in utility types:

TypeScript
type PartialUser = Partial<User>;    // all properties become optional
type ReadonlyUser = Readonly<User>;  // all properties become readonly
type UserKeys = keyof User;          // union of all property names

Enum

enum defines a named set of constants, giving related values clear names and a shared type.

Numeric Enum (default)

TypeScript
enum Direction {
  Up,    // 0
  Down,  // 1
  Left,  // 2
  Right, // 3
}

const move = Direction.Up; // 0

Members auto-increment from 0 by default.

String Enum

TypeScript
enum Status {
  Active = 'ACTIVE',
  Inactive = 'INACTIVE',
  Pending = 'PENDING',
}

const status = Status.Active; // 'ACTIVE'

String enums are more readable at runtime and easier to debug than numbers.

Const Enum

Adding const causes the enum to be inlined at compile time — no JavaScript object is generated:

TypeScript
const enum Direction {
  Up = 'UP',
  Down = 'DOWN',
}

const move = Direction.Up; // compiles to: const move = 'UP';

What Enum Compiles To

A regular enum compiles to a real JavaScript object:

TypeScript
// TypeScript
enum Status {
  Active = 'ACTIVE',
  Inactive = 'INACTIVE',
}

// Compiled JavaScript
var Status;
(function (Status) {
  Status["Active"] = "ACTIVE";
  Status["Inactive"] = "INACTIVE";
})(Status || (Status = {}));

This means the enum exists at runtime — you can iterate over it and look up names by value.


Interface vs Type

interface and type are interchangeable in many situations. The differences that matter:

interfacetype
Object shapes
Union types
Intersectionvia extendsvia &
Declaration merging✓ (same name merges)✗ (duplicate name errors)
Extension syntaxextends&
Utility typesworks as generic parameterworks as generic parameter
TypeScript
// interface style
interface Animal {
  name: string;
}
interface Dog extends Animal {
  breed: string;
}

// equivalent with type
type Animal = { name: string };
type Dog = Animal & { breed: string };

When to Use Each

Use interface when:

  • Defining the shape of an object, especially for public API types
  • The type needs to be implemented by a class
  • You want to allow third parties to extend the type via declaration merging
TypeScript
interface Printable {
  print(): void;
}

class Document implements Printable {
  print() {
    console.log('Printing...');
  }
}

Use type when:

  • Defining union or intersection types
  • Naming non-object types (functions, tuples, primitive aliases)
  • Working with utility types
TypeScript
type EventHandler = (event: MouseEvent) => void;
type Pair = [string, number];
type StringOrNumber = string | number;

Use enum when:

  • You have a fixed set of named constants (statuses, directions, error codes)
  • You need to iterate over the values or look up names at runtime
TypeScript
enum HttpStatus {
  Ok = 200,
  NotFound = 404,
  InternalServerError = 500,
}

function handleResponse(status: HttpStatus) {
  if (status === HttpStatus.Ok) {
    // ...
  }
}

When not to use enum: if you only need a set of string constants and don't need a runtime object, as const is a lighter alternative:

TypeScript
const Status = {
  Active: 'ACTIVE',
  Inactive: 'INACTIVE',
} as const;

type Status = typeof Status[keyof typeof Status];
// 'ACTIVE' | 'INACTIVE'

Summary

  • interface — defines object shapes; supports extension and declaration merging; best for public API types
  • type — type alias for anything; best for union types, intersection types, and utility types
  • enum — named constant set; compiles to a real JavaScript object; best for fixed sets of values

A practical rule of thumb: use interface for object shapes, type for union and intersection types, and enum (or as const) for fixed constant sets.