Interface vs Type in TypeScript
Interfaces and type aliases both name object shapes in TypeScript, but they differ in merging, extension style, and what they can describe. Learn the key differences and when to choose each.
TypeScript gives you two keywords, interface and type, to name an object shape. For most object types, they work the same way. The differences matter when you need declaration merging, when you want to describe something other than an object, or when you care about how errors appear.
The main distinction: an interface is open -- you can add properties to it later by declaring it again. A type alias is closed -- once declared, it cannot be changed.
Quick comparison
| Feature | Interface | Type Alias |
|---|---|---|
| Describe object shapes | Yes | Yes |
| Describe unions | No | Yes |
| Describe tuples | No | Yes |
| Rename primitives | No | Yes |
| Declaration merging | Yes | No |
| Extend syntax | extends keyword | & operator |
| Name in errors | Always | Sometimes |
Both can describe object shapes with the same property modifiers -- optional with a question mark and readonly with the readonly keyword. Both work interchangeably as parameter types, return types, and variable annotations.
Where they overlap: object shapes
For describing an object, the syntax is nearly identical. With an interface, you list properties inside curly braces using the interface keyword. With a type alias, you use the type keyword followed by an equals sign and the same object shape. Both produce the same result: the compiler checks that the object has the required properties with the correct types.
interface User {
name: string;
age: number;
}
const ada: User = { name: "Ada", age: 30 };The same shape as a type alias uses the type keyword with an equals sign instead. Both approaches produce the same result: the compiler checks that the object has the required properties with the correct types. Here is the type alias version:
type User = { name: string; age: number };
const ada2: User = { name: "Ada", age: 30 };Both approaches produce the same result. Both check that ada has name and age with the correct types. Both are erased at compile time. For this use case, the choice is purely about which keyword you prefer.
Where interfaces win: declaration merging
Interfaces can be declared multiple times in the same scope. TypeScript merges all declarations into a single interface with every property combined.
interface Window {
title: string;
}
interface Window {
version: number;
}
const win: Window = { title: "My App", version: 2 };This is called declaration merging. It lets you add properties to an existing type without modifying the original source. Library authors use it to let consumers extend built-in types. Plugin systems use it to let plugins contribute their own fields to a shared config type.
Type aliases cannot do this. Declaring the same type alias twice produces a duplicate identifier error.
Where type aliases win: non-object types
Type aliases can name any TypeScript type. Interfaces can only describe object shapes. This means type aliases are the only choice for unions, tuples, and primitive renames.
type Status = "loading" | "success" | "error";
type Point3D = [number, number, number];
type Callback = (data: string) => void;None of these can be expressed as an interface. An interface always describes a set of named properties on an object. A type alias can be anything.
How they extend differently
To build a new type from an existing one, interfaces use extends and type aliases use intersection.
interface Animal { name: string; }
interface Dog extends Animal { breed: string; }
type Animal = { name: string };
type Dog = Animal & { breed: string };Both produce a type that requires name and breed. The difference is in how conflicts are handled. If two interfaces declare the same property with different types, the compiler reports an error. If two intersected type aliases have conflicting property types, the property resolves to never -- meaning no value can satisfy it -- but the alias itself is not flagged as an error until you try to use it.
type A = { value: string };
type B = { value: number };
type AB = A & B;
const x: AB = { value: "hello" };
// Error: Type 'string' is not assignable to type 'never'.The error appears at the assignment, not at the type definition. With extends on interfaces, the error appears at the definition, which is usually easier to find and fix.
Error message clarity
Interfaces always appear by their original name in error messages. Type aliases may be expanded to their underlying type in some contexts, making errors harder to read.
Consider an error about a missing property. With an interface, you see "Type X is missing property Y from type User." With a type alias, you might see "Type X is missing property Y from type { name: string; age: number; }." The named form is easier to scan and search for.
When to use which
Use an interface when you are describing an object shape that may need to be extended later, when you want declaration merging, or when you prefer named types in error messages.
Use a type alias when the type is a union, intersection, tuple, or mapped type. Interfaces cannot describe these. Also use a type alias when the type is a one-off that will never be merged or extended.
For object shapes in your own code, the choice is mostly preference. Many teams follow a simple rule: default to interface for objects, and use type for everything else. This gives you declaration merging when you need it and the flexibility to name any type.
For deeper dives on each tool, see interfaces in TypeScript explained and type aliases in TypeScript explained.
Common mistakes
Trying to use extends on a type alias. Use an intersection with the ampersand operator instead.
Trying to declare the same type alias twice to add properties. Type aliases are closed. Use an interface if you need declaration merging.
Assuming the choice matters more than it does. For most object types, interfaces and type aliases are interchangeable. Do not spend too long deciding. Pick one, use it consistently, and move on to solving real problems.
Rune AI
Key Insights
- Interfaces support declaration merging; type aliases do not.
- Type aliases can name unions, tuples, and primitives; interfaces can only describe object shapes.
- Extend interfaces with
extends; combine type aliases with&(intersection). - Interfaces always appear by name in error messages; type aliases may not.
- For object shapes the choice is mostly preference -- pick one style and be consistent.
Frequently Asked Questions
Can I use `extends` with a type alias?
Which is better for performance, interface or type?
Can type aliases appear in error messages?
Conclusion
Interfaces and type aliases overlap heavily for object shapes. The key difference is that interfaces are open and support declaration merging, while type aliases are closed but can describe anything -- unions, intersections, tuples, and more. For object shapes, pick either and stay consistent. For anything else, reach for a type alias.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.