Types vs Interfaces
type Task = {...} and interface Task {...} both work, and teams argue about which to use. The practical answer is short: they overlap for object shapes, and each has a few things only it can do. Learn those few things and the choice becomes easy.
The rule
Interfaces describe objects and can be reopened; aliases name anything but are closed.
Where they are the same. For a plain object shape, these are interchangeable:
interface TaskA { id: number; title: string }
type TaskB = { id: number; title: string };Both support optional and readonly members, methods, and being implemented by a class. Both are erased at compile time.
Only a type alias can name something that is not an object shape:
type Status = "todo" | "doing" | "done"; // union
type Pair = [number, string]; // tuple
type TaskId = number; // primitive
type TaskFilter = (t: TaskA) => boolean; // function typeOnly an interface can be reopened. Declaring the same interface name twice merges the members, which is called declaration merging:
interface Settings { theme: string }
interface Settings { pageSize: number }
// Settings now has both theme and pageSizeDoing the same with type is a duplicate-name error. Merging is how you add fields to types you do not own, such as the browser's Window or a library's options interface.
Extending. Interfaces use extends and report conflicting members as errors. Aliases combine with &, where a conflict quietly becomes never.
Many teams settle on a simple convention: interfaces for object shapes, type aliases for everything else. Others use aliases everywhere for consistency. Either is fine if applied consistently.
The shortcut
Ask two questions in order:
- "Is it an object shape?" No → it must be a type alias.
- "Do I need to add to it from elsewhere?" Yes → it must be an interface. Otherwise, follow the team's convention.
Status
Not an object shape, it is a union. Type alias:
type Status = ....
The trap: accidentally declaring the same interface name twice in one scope and getting a merged monster instead of an error. If a type has members you never wrote, search for a second declaration.
Drills
Answer fast — trust the rule.
Which can only be written as a type alias?
What happens when interface Settings is declared twice with different members?
What happens when type Settings is declared twice in the same scope?
You need to add a tracker property to the browser's global Window type. What do you use?
Remember
- Interfaces describe objects and can be reopened; aliases name anything but are closed.
- Ask: is it an object shape? Do I need to add to it from elsewhere?
- Trap: a repeated interface name merges silently instead of erroring.
