Interfaces and Type Aliases
Learn how to define custom types using interfaces and type aliases in TypeScript
Interfaces and Type Aliases
TypeScript provides powerful ways to define custom types that help you model your data and create more maintainable code.
Interfaces
Interfaces define the shape of an object, specifying what properties it should have and their types:
interface User {
id: number;
name: string;
email: string;
isActive: boolean;
}
const user: User = {
id: 1,
name: "John Doe",
email: "john@example.com",
isActive: true
};Optional Properties
Use the ? operator to make properties optional:
interface UserProfile {
id: number;
name: string;
email?: string; // Optional
avatar?: string; // Optional
}
const profile: UserProfile = {
id: 1,
name: "Jane Doe"
// email and avatar are optional
};Readonly Properties
Mark properties as readonly to prevent modification after creation:
interface Point {
readonly x: number;
readonly y: number;
}
const point: Point = { x: 10, y: 20 };
// point.x = 5; // Error: Cannot assign to 'x' because it is a read-only propertyFunction Types in Interfaces
Interfaces can describe function types:
interface SearchFunction {
(source: string, subString: string): boolean;
}
const mySearch: SearchFunction = function(source: string, subString: string): boolean {
return source.search(subString) > -1;
};Extending Interfaces
Interfaces can extend other interfaces:
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
bark(): void;
}
const myDog: Dog = {
name: "Buddy",
age: 3,
breed: "Golden Retriever",
bark() {
console.log("Woof!");
}
};Type Aliases
Type aliases create a new name for a type:
type ID = number | string;
type User = {
id: ID;
name: string;
email: string;
};
// Union types
type Status = "pending" | "approved" | "rejected";
// Function types
type EventHandler = (event: Event) => void;
// Generic type aliases
type ApiResponse<T> = {
data: T;
status: number;
message: string;
};Interface vs Type Alias
Both can be used to define object shapes, but there are differences:
// Interface - can be extended and merged
interface UserInterface {
name: string;
}
interface UserInterface {
age: number; // Declaration merging
}
// Type alias - more flexible with unions and primitives
type UserType = {
name: string;
} & {
age: number; // Intersection
};
type StringOrNumber = string | number; // Union typesIndex Signatures
For objects with dynamic property names:
interface StringDictionary {
[key: string]: string;
}
const colors: StringDictionary = {
red: "#FF0000",
green: "#00FF00",
blue: "#0000FF"
};Nested Interfaces
Interfaces can contain other interfaces:
interface Address {
street: string;
city: string;
country: string;
}
interface Company {
name: string;
address: Address;
employees: User[];
}Best Practices
- Use interfaces for object shapes: Especially when you might need to extend them
- Use type aliases for unions and primitives: More flexible for complex types
- Prefer composition over deep nesting: Keep interfaces focused and composable
- Use descriptive names: Make your types self-documenting
Exercise
Create an interface for a blog post that includes:
- Required: id, title, content, author (User interface), publishedAt
- Optional: tags (array of strings), featuredImage
Then create a type alias for different post statuses and add it to your interface.
Next lesson: We'll explore classes and how they work with TypeScript's type system!