Articles

TypeScript interface vs type: What's the Difference

TypeScript's interface and type both describe object shapes, but they diverge on declaration merging, unions, and extension. When to reach for each.

Takina Takina · · 4 min read
Dark-themed code editor showing TypeScript type definitions

TypeScript gives you two ways to describe the shape of an object: an interface or a type alias. For plain object shapes they’re nearly interchangeable, but they diverge in a few places that matter — declaration merging, what they can describe, and how error messages read. Neither is strictly better; the right choice depends on what you’re modeling.

The basic overlap

Both can describe the same object shape with identical syntax at the call site:

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

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

A function that accepts one accepts the other. TypeScript’s structural type system doesn’t care which keyword produced the shape — only whether the shape matches. If you’re new to the type system generally, TypeScript generics and the utility types built on top of Partial, Pick, and Omit work identically whether you feed them an interface or a type.

Where they diverge

Declaration merging

An interface can be declared multiple times with the same name, and TypeScript merges the declarations into one:

interface Window {
  myGlobal: string;
}

interface Window {
  anotherGlobal: number;
}
// Window now has both myGlobal and anotherGlobal

A type alias cannot be redeclared — doing so is a compile error. This makes interface the right tool for augmenting types you don’t own, like extending a third-party library’s ambient types or adding a property to the global Window object.

What each can express

A type alias can describe things an interface cannot: unions, tuples, mapped types, and conditional types.

type Status = "pending" | "active" | "closed";
type Pair = [string, number];
type Handler = (event: string) => void;

There’s no interface equivalent for a union of string literals or a tuple. If you’re modeling anything other than a plain object — a union, a function signature, a mapped type derived from another type — type is the only option. See discriminated unions and mapped types for common patterns that require type.

Extension syntax

Both support extension, with different syntax:

interface Animal {
  name: string;
}
interface Dog extends Animal {
  breed: string;
}

type AnimalType = {
  name: string;
};
type DogType = AnimalType & {
  breed: string;
};

interface extends checks that the extended shape is compatible and errors early with a clear message if it isn’t. Intersection (&) on type will still combine the shapes, but conflicting property types can silently collapse to never instead of raising an error at the point of extension — the mismatch only surfaces later, when you try to use the property.

Comparison table

interfacetype
Object shapesYesYes
UnionsNoYes
TuplesNoYes
Declaration mergingYesNo
Extensionextends (checked eagerly)& intersection (checked on use)
Function/constructor signaturesYes, via call signaturesYes, more common in practice
Implements (classes)YesYes

Which one should you use

For a plain object shape that you expect other code to extend or augment — a public API’s parameter type, a class’s implemented shape, anything a consumer of your library might need to add fields to — interface gives you merging and slightly better error messages on conflicting extensions.

For anything else — unions, tuples, function types, types derived from other types via mapping or conditionals — type is the only choice, since interface can’t express them.

A common convention is to default to interface for object shapes and reach for type only when you need a feature interface doesn’t have. Other codebases standardize on type everywhere for consistency, since it’s a strict superset of what interface can express except merging. Either convention is defensible; what matters is picking one and applying it consistently, since mixing both for the same kind of shape adds cognitive overhead without a functional benefit. If your team enforces a house style, tools like ESLint can lint for it automatically.

One thing that doesn’t matter for this decision: runtime performance. Both compile away entirely — TypeScript’s type system is erased at build time, so interface and type have zero runtime cost regardless of which you choose.

The takeaway

interface and type overlap heavily for plain object shapes, but interface supports declaration merging and gives eager error checking on extension, while type is the only option for unions, tuples, and other non-object shapes. Use interface when you’re defining an object shape that might need to merge or be extended by consumers; use type for everything else. Pick a default for plain object shapes and stay consistent — the compiler doesn’t have a preference, but your codebase’s readability benefits from having one.

Takina Takina · · 4 min read

TypeScript Abstract Classes, Explained

Abstract classes in TypeScript define shared implementation plus methods subclasses must fill in. How they differ from interfaces and when to reach for them.

#TypeScript #JavaScript #Web Development
Takina Takina · · 3 min read

TypeScript's never Type, Explained

never represents values that can't exist — it marks unreachable code, exhaustive switches, and functions that always throw or loop forever.

#TypeScript #JavaScript #Web Development
Takina Takina · · 4 min read

TypeScript readonly Modifiers Explained

TypeScript's readonly keyword blocks reassignment at compile time for properties, arrays, and tuples — with no runtime enforcement at all.

#TypeScript #JavaScript #Web Development