Type Aliases and Interfaces are used to create custom types in TypeScript. They allow you to define complex data structures once and reuse them throughout your codebase, making your code cleaner and more predictable.
Type Alias
A Type Alias gives a specific name to a type definition. You can use it in place of standard types like string, number, or boolean. Type Aliases are versatile and can represent primitives, objects, arrays, union types, or functions.
-
Union Type Alias:
type UserID = string | number; let id1: UserID = 123; // ✅ Works let id2: UserID = "ABC"; // ✅ Works -
Object Type Alias:
type User = { name: string; age: number; }; const user1: User = { name: "Ali", age: 25 };
Extending Type Aliases with Intersections
Type Aliases cannot use the extends keyword, but they can be combined using the intersection operator (&), which merges two or more types into one.
type Person = {
name: string;
age: number;
};
type Employee = Person & {
position: string;
};
const emp1: Employee = {
name: "Ali",
age: 30,
position: "Developer"
}; // ✅ Must include all properties from both Person and Employee
Interfaces
An Interface is similar to a Type Alias but is specifically designed for defining the shape of objects. While Type Aliases can represent any type (like a union of strings), Interfaces are strictly for object structures.
interface Person {
name: string;
age: number;
}
const person1: Person = { name: "Ali", age: 25 };
Extending Interfaces
Interfaces can be extended using the extends keyword. This allows one interface to inherit properties from another, making it easy to build complex structures from simpler ones.
interface Employee extends Person {
position: string;
}
const emp1: Employee = {
name: "Ali",
age: 30,
position: "Developer"
};
Declaration Merging
A behaviour unique to interfaces is declaration merging — if two interfaces share the same name, TypeScript automatically combines them into one. This is something Type Aliases cannot do; redeclaring a type alias with the same name throws an error.
interface Car {
brand: string;
}
interface Car {
model: string;
}
// TypeScript merges both into one:
const myCar: Car = { brand: "Toyota", model: "Corolla" }; // ✅
This is particularly useful when extending third-party library types without modifying the original source.
Readonly & Optional Properties
You can further control how properties behave within an interface or type:
readonly: Prevents a property from being changed after it is first assigned.?(Optional): Indicates that a property is not required when creating the object.
interface Car {
readonly id: number;
brand: string;
model?: string; // Optional
}
const car1: Car = { id: 1, brand: "Toyota" };
car1.id = 2; // ❌ Error: Cannot assign to 'id' because it is a read-only property.
Function Types in Interfaces
Interfaces can also define the structure of a function, ensuring that any function assigned to that type follows a specific pattern of inputs and outputs.
interface MathOperation {
(x: number, y: number): number;
}
const multiply: MathOperation = (a, b) => a * b;
console.log(multiply(5, 3)); // ✅ 15
Type Alias vs. Interface: Quick Comparison
| Feature | Type Alias | Interface |
|---|---|---|
| Usage | Primitives, Unions, Objects, Tuples | Objects and Functions only |
| Extending | Via Intersections (&) | Via extends keyword |
| Merging | Cannot be redeclared after creation | Can be merged via Declaration Merging |