Question
In TypeScript, what is the difference between using an interface and using a type alias for the following object shape?
interface X {
a: number;
b: string;
}
type X = {
a: number;
b: string;
};
Both appear to describe the same structure. When should each one be used, and how do their behaviors differ in practice?
Short Answer
By the end of this page, you will understand how interface and type alias work in TypeScript, what they have in common, where they differ, and which one is usually better for a given situation. You will also see how these choices affect extension, unions, declaration merging, and real project structure.
Concept
In TypeScript, both interface and type can describe the shape of data.
For simple object shapes, they often look interchangeable:
interface User {
name: string;
age: number;
}
type User = {
name: string;
age: number;
};
In both cases, TypeScript checks whether a value has the required properties with the correct types.
What they have in common
Both can:
- describe object shapes
- be used for function parameters
- be used for return types
- be extended in some way
- work with TypeScript's structural typing system
That means TypeScript mostly cares about the shape of a value, not whether it came from an interface or a type alias.
The main difference
The biggest difference is this:
interfaceis mainly for describing object-like structures and supports declaration mergingtypeis an alias for any type expression, including primitives, unions, tuples, intersections, and mapped types
interface is specialized
An interface is best thought of as a named contract for an object or class.
interface Product {
id: number;
name: string;
}
It is focused on object structure and is very common in APIs, models, and class contracts.
type is more flexible
A type alias can name almost any type, not just object shapes.
type ID = string | number;
type Point = [number, number];
type Status = "loading" | "success" | "error";
You cannot do those with interface.
Declaration merging
One unique feature of interface is that multiple declarations with the same name are merged.
interface Person {
name: string;
}
interface Person {
age: number;
}
const p: Person = {
name: "Ava",
age: 30
};
This works because TypeScript combines both interface declarations.
The same is not true for type:
type Person = {
name: string;
};
// Error: Duplicate identifier 'Person'
type Person = {
age: number;
};
This matters in library authoring and when augmenting existing types.
Extending and combining
Interfaces use extends:
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
Types usually combine with intersections using &:
type Animal = {
name: string;
};
type Dog = Animal & {
breed: string;
};
Both can achieve similar results for object composition.
Why this matters in real programming
Choosing between interface and type affects:
- how your types evolve over time
- whether you need unions or tuples
- whether consumers may augment the type later
- how readable and maintainable your code feels
A common rule of thumb is:
- use
interfacefor object-shaped contracts you expect to extend or implement - use
typewhen you need unions, intersections, primitives, tuples, or more advanced type composition
Mental Model
Think of interface as a blueprint for an object.
It says, "Anything calling itself this shape must have these parts."
Think of type as a label you can attach to any kind of type.
It can label:
- an object shape
- a string literal union
- a tuple
- a primitive alias
- a combination of other types
So:
interface= blueprint for object-like structurestype= nickname for any type expression
If you are naming a house layout, interface is the house blueprint.
If you are naming anything at all—house, color, status, coordinates, or a combination—type is the more general label.
Syntax and Examples
Basic object shape with interface
interface User {
name: string;
age: number;
}
const user: User = {
name: "Mia",
age: 25
};
This defines a contract for objects with name and age.
Basic object shape with type
type User = {
name: string;
age: number;
};
const user: User = {
name: "Mia",
age: 25
};
For this case, the result is effectively the same.
type can represent more than objects
type ID = string | number;
type Result = "success" | "error";
type Coordinates = [number, number];
These are valid type aliases, but they cannot be written as interfaces.
Extending an interface
interface Vehicle {
brand: string;
}
interface Car extends Vehicle {
doors: number;
}
const car: Car = {
brand: "Toyota",
doors: 4
};
Combining types with &
type Vehicle = {
brand: string;
};
type Car = Vehicle & {
doors: number;
};
const car: Car = {
brand: "Toyota",
doors: 4
};
Declaration merging with interfaces
interface Settings {
theme: string;
}
interface Settings {
language: string;
}
const settings: Settings = {
theme: "dark",
language: "en"
};
This works because interfaces merge.
Duplicate type aliases do not merge
type Settings = {
theme: string;
};
// Error
type Settings = {
language: string;
};
Step by Step Execution
Consider this code:
interface X {
a: number;
b: string;
}
const value: X = {
a: 10,
b: "hello"
};
Here is what TypeScript does:
- It reads the
interface Xdeclaration. - It stores the rule that an
Xobject must have:aas anumberbas astring
- It sees
const value: Xand knowsvaluemust match that shape. - It checks the object literal:
a: 10→ valid, because10is a numberb: "hello"→ valid, because"hello"is a string
- The assignment succeeds.
Now the same idea with a type alias:
type X = {
a: number;
b: string;
};
const value: X = {
a: 10,
b: "hello"
};
Step by step:
- TypeScript reads
type X = .... - It treats
Xas a name for that object type. - It sees
const value: X. - It checks whether the object has the required properties.
- The assignment succeeds for the same reason.
For this exact example, both behave the same during type checking.
Now compare a case where they differ:
interface A {
name: string;
}
interface A {
age: number;
}
const person: A = {
name: "Lina",
age: 20
};
Step by step:
- TypeScript reads the first
interface A. - It reads the second
interface A. - Because interfaces merge, it combines them.
- Final shape becomes:
{
name: string;
age: number;
}
personis valid.
This is one of the key practical differences.
Real World Use Cases
API response models
Interfaces are often used to describe response objects:
interface ApiUser {
id: number;
email: string;
isAdmin: boolean;
}
This makes it easy to type API data consistently.
Union states in UI code
Type aliases are commonly used for fixed sets of possible values:
type RequestState = "idle" | "loading" | "success" | "error";
This is useful in React, frontend apps, and async workflows.
Function parameter objects
Both are used for configuration objects:
interface FetchOptions {
timeout: number;
retries: number;
}
function fetchData(options: FetchOptions) {
// ...
}
Tuples for coordinates or pairs
Type aliases are ideal for tuple structures:
type LatLng = [number, number];
Library augmentation
Interfaces are used when a library allows extra fields to be added later through declaration merging. This is common in TypeScript ecosystem typings.
Real Codebase Usage
In real codebases, teams usually apply a few simple patterns.
Use interface for domain objects and contracts
Common examples:
UserProductApiResponseFormValues- class contracts with
implements
interface User {
id: number;
name: string;
}
class AdminUser implements User {
id: number;
name: string;
constructor(id: number, name: string) {
this.id = id;
this.name = name;
}
}
Use type for unions and utility composition
type Theme = "light" | "dark";
type ApiResult<T> =
| { ok: true; data: T }
| { ok: false; error: string };
This pattern is very common in error handling and state modeling.
Compose types for reusable pieces
type WithTimestamps = {
createdAt: Date;
updatedAt: Date;
};
type Post = WithTimestamps & {
id: number;
title: string;
};
Validation and guard patterns
Types are often paired with runtime checks:
interface User {
id: number;
name: string;
}
function isUser(value: any): value is User {
return (
typeof value === "object" &&
value !== null &&
typeof value.id === "number" &&
typeof value.name === "string"
);
}
Team convention matters
Many teams choose one default style for object shapes to keep code consistent. A common convention is:
interfacefor plain object contractstypefor everything else
The most important thing is consistency and choosing the one that fits the job.
Common Mistakes
1. Thinking they are always different in behavior
For simple object shapes, they are often effectively the same.
interface User {
name: string;
}
type UserAlias = {
name: string;
};
Do not assume one changes runtime behavior. Both are erased at compile time.
2. Trying to use interface for a union
Broken code:
// Invalid idea for interface
// interface Status = "open" | "closed";
Use a type alias instead:
type Status = "open" | "closed";
3. Forgetting that interfaces can merge
This can be helpful, but it can also be surprising if the same name appears twice.
interface Config {
port: number;
}
interface Config {
host: string;
}
Now Config requires both port and host.
4. Expecting duplicate type names to merge
Broken code:
type Config = {
port: number;
};
type Config = {
host: string;
};
This causes a duplicate identifier error.
5. Confusing extends with &
Interfaces usually extend with extends:
interface A {
x: number;
}
interface B extends A {
y: number;
}
Types often combine with intersection:
type A = { x: number };
type B = A & { y: number };
6. Ignoring readability
Sometimes both options work, but one communicates intent better.
- If it is a shared object contract,
interfacemay read more clearly. - If it is a union or composed type expression,
typeis usually clearer.
Comparisons
| Feature | interface | type |
|---|---|---|
| Describes object shapes | Yes | Yes |
| Can alias primitives | No | Yes |
| Can alias unions | No | Yes |
| Can alias tuples | No | Yes |
| Supports declaration merging | Yes | No |
| Can be extended | Yes, with extends | Yes, with & and sometimes extends in interfaces using a type |
Common for class implements | Yes | Yes, for object-like shapes |
| Best for object contracts | Often | Also possible |
| Best for advanced type composition | Limited | Yes |
Quick rule of thumb
- Use
interfacewhen defining a named object contract. - Use
typewhen you need more than an object shape.
Example side by side
interface Person {
name: string;
age: number;
}
type PersonShape = {
name: string;
age: number;
};
These are similar.
type Status = "pending" | "done";
This must be a type, not an interface.
Cheat Sheet
Quick reference
Use interface when
- defining object-shaped contracts
- describing models like
User,Product,Order - you want declaration merging
- you want a clear contract for classes
interface User {
id: number;
name: string;
}
Use type when
- naming a union
- naming a tuple
- aliasing a primitive
- composing types with
& - creating more advanced type expressions
type ID = string | number;
type Point = [number, number];
type Theme = "light" | "dark";
Similarity
For plain object shapes, both often work:
interface A {
x: number;
}
type B = {
x: number;
};
Important difference
Interfaces merge:
interface A {
x: number;
}
interface A {
y: number;
}
Type aliases do not:
type A = { x: number };
// type A = { y: number }; // Error
Memory aid
interface= object contracttype= any type label
FAQ
Is interface better than type in TypeScript?
Not universally. interface is often better for object contracts, while type is better for unions, tuples, and advanced type composition.
Are interface and type the same for objects?
For many simple object shapes, yes, they behave very similarly during type checking.
Can a class implement a type alias?
Yes, if the type alias resolves to an object-like shape.
type Person = {
name: string;
};
class User implements Person {
name = "Sam";
}
Can type do something interface cannot?
Yes. type can represent unions, tuples, primitive aliases, and many composed type expressions.
Can interface do something type cannot?
Yes. Interfaces support declaration merging.
Should I always use one and avoid the other?
No. Most real TypeScript projects use both. Choose based on what you are modeling.
Which should beginners use first?
Start with interface for plain object shapes and type for unions and tuples. That rule works well in most beginner projects.
Mini Project
Description
Build a small TypeScript model for a blog system to practice when to use interface and when to use type. This project demonstrates a realistic mix: object contracts for entities and type aliases for status values and API result states.
Goal
Create typed blog entities and a typed API result model using both interface and type appropriately.
Requirements
- Create an
interfacefor a blog post withid,title, andcontent. - Create a
typealias for post status using string literals such as"draft"and"published". - Create a combined type for a post plus its status.
- Create a generic API result type for success or failure.
- Write a function that returns a typed API result for a sample post.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.