ESC

Type to search the knowledge base.

Interfaces vs Type Aliases

When to use interface vs type in TypeScript — extension, unions, declaration merging, and a practical default for frontend apps.

beginner3 min read
  • typescript
  • interfaces-vs

Both interface and type can describe object shapes. Interviews love “which one?” The honest answer: mostly interchangeable for object types; differences matter for unions, tuples, mapped types, and declaration merging.

Docs: Object Types, Everyday Types.

Same job, two spellings

interface UserI {
  id: string;
  name: string;
}

type UserT = {
  id: string;
  name: string;
};

const u1: UserI = { id: '1', name: 'Ada' };
const u2: UserT = { id: '1', name: 'Ada' };

For props, DTOs, and domain models, either works if the team is consistent.

What only type does well

Unions and intersections as top-level aliases:

type Status = 'idle' | 'loading' | 'done';
type Result<T> = { ok: true; value: T } | { ok: false; error: Error };
type Id = string | number;

Interfaces cannot express a union of primitives or a union of object variants as the interface itself (you can still use interfaces inside a union).

Tuples, primitives, mapped wrappers:

type Pair = [string, number];
type ReadonlyUser = Readonly<UserT>;
type Flags = { [K in 'a' | 'b']: boolean };

Function type alias:

type ClickHandler = (event: MouseEvent) => void;

(Interfaces can describe call signatures too, but type aliases read more naturally for pure functions.)

What only interface does well

Declaration merging — same name, multiple blocks combine:

interface Window {
  __APP_VERSION__?: string;
}

Library authors use this for augmentation. App code rarely needs merging; accidental merges can surprise you.

extends with clear OOP-style hierarchies:

interface Timestamped {
  createdAt: string;
}

interface Entity extends Timestamped {
  id: string;
}

type uses intersections:

type Entity = Timestamped & { id: string };

Both are valid; interfaces can error more clearly on conflicting extends in some cases.

Extending vs intersecting

interface A {
  x: string;
}
interface B extends A {
  y: number;
}

type C = A & { y: number };

For simple object extension, prefer one style. Intersection can produce never fields when properties conflict oddly — know how to read that error.

React props convention

Many codebases:

type ButtonProps = {
  variant?: 'primary' | 'secondary';
  children: React.ReactNode;
  onClick?: () => void;
};

Others use interface ButtonProps. React’s own types historically lean interfaces for merging. Either is fine; don’t mix randomly in one component folder.

Performance / checking myths

Old lore said interfaces check faster. For app-sized codebases the difference is noise. Choose for expressiveness and team consistency, not microbenchmark folklore.

Practical default (frontend)

Use case Prefer
Object props / domain models type or interface (team rule)
Unions, discriminated states type
Tuples, primitives, utilities type
Public lib augmentation interface merge
Mapped / conditional compositions type

A solid team rule: type by default; interface when you need merging or a public extendable contract.

Footguns

  1. Merging accidentally — two interface User in a project combine; with type you get a duplicate identifier error (often better).
  2. interface for unions — can’t; switch to type.
  3. Religious rewrites — converting 200 interfaces to types in a PR adds risk for no product value.

Interview out-loud answer

“For object shapes they’re nearly the same. I use type for unions, tuples, and mapped types. I use interface when declaration merging or a clearly extendable public object contract matters. Consistency beats pedantry.”

Further reading

Related guides