Reading — step 1 of 7
Learn
Both type and interface describe the shape of values. They overlap heavily — but each has cases where it shines. The TypeScript Handbook's recommendation: use whichever fits, lean toward interface for object shapes that may extend.
Interface
interface User {
name: string;
age: number;
greet(): string;
}
const ada: User = {
name: 'Ada',
age: 36,
greet() { return `Hi, I'm ${this.name}`; }
};
Inheritance with extends:
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
const rex: Dog = { name: 'Rex', breed: 'Husky' }; // both fields required
Class implementation:
class Logger implements Loggable {
log(msg: string) { console.log(msg); }
}
Type alias
type User = {
name: string;
age: number;
greet(): string;
};
Looks similar — but type can describe MORE:
type StringOrNumber = string | number; // unions — interface CANNOT
type Pair<T> = [T, T]; // tuples
type Callback = (x: number) => void; // function shorthand
type Brand = string & { __brand: 'BrandedString' }; // intersections
type Tree<T> = { value: T; children: Tree<T>[] }; // recursive
Composition with intersection:
type Animal = { name: string };
type Dog = Animal & { breed: string };
const rex: Dog = { name: 'Rex', breed: 'Husky' };
Similar effect to extends — but using intersection.
The big differences
Interfaces are open — declaration merging
Multiple interface declarations of the same name automatically merge:
interface Window {
customField: string;
}
// Elsewhere — usually in a .d.ts file
interface Window {
anotherField: number;
}
// window now has both
window.customField;
window.anotherField;
This is HOW you augment global types like Window or Express.Request. Type aliases CANNOT merge — duplicate type Foo = ... is an error.
Types are closed — but more flexible
type Status = 'on' | 'off'; // ✓
interface Status = 'on' | 'off'; // ✗ — interface can't be a union
type Coords = [number, number]; // ✓
interface Coords { 0: number; 1: number; length: 2 } // awkward
Performance (in big codebases)
For pure object shapes that extend, interfaces are slightly faster for the type checker — TypeScript caches their resolution. With 100k+ types, it adds up.
For everyday code: pick whatever reads better.
When to use which
Use interface for:
- Public APIs you want consumers to extend or augment
- Class contracts (
implements MyInterface) - Object shapes where you might add fields later
- Anything in
.d.tsfiles for declaration merging
Use type for:
- Unions, intersections, tuples, function types
- Mapped/conditional/template-literal types
- Branded types
- Anything that needs the "type-only" expressiveness
Common mistakes
- Trying to declare-merge a
typealias — error. Use interface. - Using interface for unions —
interface X = A | Bis a syntax error. Use type. - Naming interfaces
IUser— historic C#-ism, no longer recommended in modern TS. JustUser. - Forgetting that
extendswith interface vs&with type behave slightly differently for conflicting fields — interface extends errors on conflict; intersection silently producesnever. - Mixing types and interfaces inconsistently in one codebase — pick conventions and stick to them.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…