AcademyTypeScript
Type aliases vs interfaces
What the two actually differ on — declaration merging, unions, and the error messages you get back — and the rule that settles it in a real codebase.
3 min readUpdated 3 exercises
The short answer
For an object shape, they are interchangeable. Both of these describe the same thing, and both are assignable to each other:
interface User {
id: string;
email: string;
}
type UserAlias = {
id: string;
email: string;
};The differences only show up at the edges, and there are exactly three that matter. Everything else you have read about performance or "interfaces are for classes" is folklore.
Only an interface merges
Declare an interface twice and TypeScript merges the two declarations. Declare a type alias twice and it is an error.
interface Window {
myAnalytics: (event: string) => void;
}
// Elsewhere, Window still has every built-in property — plus this one.This is the reason interfaces exist in the type system and the reason library authors use them: merging is how you extend a type you do not own. It is also the reason not to use one for a shape internal to your app. A type nobody can reopen is a type nobody can quietly change from another file.
type User = { id: string };
type User = { name: string }; // Error: Duplicate identifier 'User'That error is a feature.
Only a type can be something other than an object
A type alias names any type. An interface only ever describes an object shape.
type Status = 'idle' | 'loading' | 'failed'; // union
type Pair = [number, number]; // tuple
type Handler = (event: Event) => void; // function
type Keys = keyof User; // computed
type Nullable<T> = T | null; // generic over anythingNone of those can be written as an interface. In practice this is what decides most of it: the moment a type is a union — and in TypeScript, a great many are — the question answers itself.
Extending is the same thing twice
interface B extends A and type B = A & { ... } produce the same assignability. The one real difference is what happens on a conflict.
interface A { id: string }
interface B extends A { id: number }
// Error: Interface 'B' incorrectly extends 'A'.
type C = { id: string } & { id: number };
// No error. C's id is 'string & number' — which is never.The interface tells you at the declaration. The intersection tells you later, at the call site, with a message about never that reads like a puzzle. When you are combining shapes you expect to be compatible, the loud version is worth having.
Both work with implements, by the way — a class can implement a type alias just as well as an interface, as long as the alias describes an object.
Which to use
| Situation | Use |
|---|---|
| A union, tuple, function type, or anything computed | type |
| A public API others may need to augment | interface |
| An object shape inside your own app | either — pick one and be consistent |
| Props for a React component | either; type composes more predictably |
| Extending a type from a dependency | interface, for the merging |
The rule that survives code review: `type` by default, `interface` when you need merging. It gives one answer in the common case and a reason for the exception, which is all a convention has to do.
Now practice it
2 challenges · 1 quizReading this page is the cheap half. These are the exercises that make you use it.
- Interfaces — implements & extendsProPuts extends and implements side by side, which is where the two declarations stop being interchangeable.ChallengeSolve · EasyChallengeSolve · Easy
- Type Annotations — Functions & ObjectsThe shapes this page compares, written out until annotating an object is something you do without thinking.ChallengeSolve · EasyChallengeSolve · Easy
- Classes & Interfaces · Classes & InterfacesClasses and interfaces questions, including what implements actually checks and what it does not.QuizTake · EasyQuizTake · Easy
Read next
Structural typing: shapes, not names
TypeScript compares shapes, not names. That is why an unrelated object type-checks, why excess properties sometimes error, and how to make a type nothing else can satisfy.
3 min
Literal types, unions and as const
Why a string in a variable stops being a literal, what as const freezes, and why a union of strings beats an enum almost every time.
3 min