Interfaces and type aliases both describe the shape of objects — what properties they have and what types those properties are. They are nearly interchangeable in modern TypeScript, with a few differences.
Interfaces
An interface defines the contract that an object must follow:
TypeScript
interface User {
name: string;
age: number;
email?: string; // optional — may or may not exist
}Now any variable typed as User must have name and age:
TypeScript
const ram: User = {
name: "Ram",
age: 22,
// email is optional — no error if omitted
};
const sita: User = {
name: "Sita",
age: 21,
email: "sita@example.com",
};
// ❌ Missing required property
const bad: User = { name: "Hari" };
// Error: Property 'age' is missingOptional Properties
Add ? after the property name to make it optional:
TypeScript
interface Product {
name: string;
price: number;
description?: string; // optional
rating?: number; // optional
}Readonly Properties
Use readonly to prevent modification after creation:
TypeScript
interface Config {
readonly apiUrl: string;
readonly timeout: number;
}
const config: Config = { apiUrl: "https://api.example.com", timeout: 5000 };
config.apiUrl = "https://other.com"; // Error: Cannot assign to 'apiUrl'Extending Interfaces
Interfaces can inherit from other interfaces with extends:
TypeScript
interface Animal {
name: string;
sound: string;
}
interface Dog extends Animal {
breed: string;
fetch(): void;
}
const rex: Dog = {
name: "Rex",
sound: "Woof",
breed: "Labrador",
fetch() { console.log("Fetching!"); },
};You can extend multiple interfaces:
TypeScript
interface HasName {
name: string;
}
interface HasAge {
age: number;
}
interface Person extends HasName, HasAge {
email: string;
}Type Aliases
Type aliases use the type keyword and can do everything interfaces
can, plus more:
TypeScript
type User = {
name: string;
age: number;
email?: string;
};Type Aliases Can Do More
| Feature | Interface | Type Alias |
|---|---|---|
| Object shapes | ✅ | ✅ |
| Extends / inherits | extends keyword |
& intersection |
| Declaration merging | ✅ Yes | ❌ No |
| Union types | ❌ No | ✅ string | number |
| Primitive aliases | ❌ No | ✅ type ID = string |
| Tuple types | ❌ No | ✅ [string, number] |
| Computed types | ❌ No | ✅ type Keys = keyof User |
TypeScript
// Type alias for a union
type Status = "active" | "inactive" | "banned";
// Type alias for a tuple
type Pair = [string, number];
// Type alias for a function signature
type Callback = (error: Error | null, data?: string) => void;Interface vs Type Alias — When to Use Which
Use interface when:
- Defining the shape of an object
- You might need declaration merging (e.g. augmenting third-party types)
- Defining contracts for classes
Use type when:
- Creating union types (
string | number) - Creating intersection types (
A & B) - Creating utility types (
Partial<T>,Pick<T, K>) - Aliasing primitive or tuple types
Index Signatures
When you do not know the property names ahead of time:
TypeScript
interface Dictionary {
[key: string]: string;
}
const translations: Dictionary = {
hello: "नमस्ते",
goodbye: "अलविदा",
};Nested Interfaces
TypeScript
interface Address {
street: string;
city: string;
country: string;
}
interface User {
name: string;
age: number;
address: Address;
}
const user: User = {
name: "Sita",
age: 21,
address: {
street: "Baneshwor",
city: "Kathmandu",
country: "Nepal",
},
};Tips
- Start with
interfacefor object shapes — it is the most common pattern and allows declaration merging. - Use
typefor unions, intersections, and computed types. - Name interfaces with PascalCase nouns (
User,Product,ApiResponse). - Use optional properties (
?) sparingly — prefer explicitnullin function parameters.
Next: functions — where TypeScript's type system really shines.