Question

Interfaces vs Type Aliases in TypeScript

typescriptinterfacetypescript-types

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:

  • interface is mainly for describing object-like structures and supports declaration merging
  • type is 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 interface for object-shaped contracts you expect to extend or implement
  • use type when 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 structures
  • type = 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.

Take Quiz

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:

  1. It reads the interface X declaration.
  2. It stores the rule that an X object must have:
    • a as a number
    • b as a string
  3. It sees const value: X and knows value must match that shape.
  4. It checks the object literal:
    • a: 10 → valid, because 10 is a number
    • b: "hello" → valid, because "hello" is a string
  5. 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:

  1. TypeScript reads type X = ....
  2. It treats X as a name for that object type.
  3. It sees const value: X.
  4. It checks whether the object has the required properties.
  5. 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:

  1. TypeScript reads the first interface A.
  2. It reads the second interface A.
  3. Because interfaces merge, it combines them.
  4. Final shape becomes:
{
  name: string;
  age: number;
}
  1. person is 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.

Take Quiz

Real Codebase Usage

In real codebases, teams usually apply a few simple patterns.

Use interface for domain objects and contracts

Common examples:

  • User
  • Product
  • ApiResponse
  • FormValues
  • 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:

  • interface for plain object contracts
  • type for 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, interface may read more clearly.
  • If it is a union or composed type expression, type is usually clearer.

Comparisons

Featureinterfacetype
Describes object shapesYesYes
Can alias primitivesNoYes
Can alias unionsNoYes
Can alias tuplesNoYes
Supports declaration mergingYesNo
Can be extendedYes, with extendsYes, with & and sometimes extends in interfaces using a type
Common for class implementsYesYes, for object-like shapes
Best for object contractsOftenAlso possible
Best for advanced type compositionLimitedYes

Quick rule of thumb

  • Use interface when defining a named object contract.
  • Use type when 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.

Take Quiz

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 contract
  • type = 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.

Take Quiz

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 interface for a blog post with id, title, and content.
  • Create a type alias 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.
Take Quiz

Keep learning

Related questions

View more