Interfaces define the shape of objects and provide a way to enforce structure in your code.
Basic Interface
interface User {
name: string;
age: number;
email: string;
}
const user: User = {
name: "Alice",
age: 25,
email: "alice@example.com"
};
Optional Properties
Use ? to make properties optional.
interface User {
name: string;
age: number;
email?: string; // Optional
}
const user1: User = {
name: "Alice",
age: 25
// email is optional, so we can omit it
};
const user2: User = {
name: "Bob",
age: 30,
email: "bob@example.com"
};
Readonly Properties
Make properties immutable.
interface Point {
readonly x: number;
readonly y: number;
}
const point: Point = { x: 10, y: 20 };
// point.x = 30; // ❌ Error: Cannot assign to 'x' because it is a read-only property
Function Types in Interfaces
interface Calculator {
add(a: number, b: number): number;
subtract(a: number, b: number): number;
}
const calc: Calculator = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
}
};
Index Signatures
Allow objects to have additional properties.
interface Dictionary {
[key: string]: string;
}
const dict: Dictionary = {
hello: "world",
foo: "bar",
// Can add any string key-value pairs
};
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!");
}
};
Multiple Inheritance
Interfaces can extend multiple interfaces.
interface Flyable {
fly(): void;
}
interface Swimmable {
swim(): void;
}
interface Duck extends Flyable, Swimmable {
quack(): void;
}
const duck: Duck = {
fly() {
console.log("Flying...");
},
swim() {
console.log("Swimming...");
},
quack() {
console.log("Quack!");
}
};
Interface vs Type Alias
Both can be used similarly, but have differences.
// Interface
interface User {
name: string;
}
// Type alias
type User = {
name: string;
};
// Interfaces can be merged (declaration merging)
interface User {
age: number; // Adds to existing User interface
}
// Type aliases cannot be merged
Exercises
Exercise 1: Basic Interface
// Create an interface called 'Book' with:
// - title (string)
// - author (string)
// - pages (number)
// - isRead (boolean)
Exercise 2: Optional Properties
// Create an interface called 'Product' with:
// - name (string, required)
// - price (number, required)
// - description (string, optional)
// - inStock (boolean, optional)
Exercise 3: Readonly Properties
// Create an interface called 'Config' with:
// - apiUrl (string, readonly)
// - timeout (number, readonly)
// - retries (number, not readonly)
Exercise 4: Function Types
// Create an interface called 'Logger' with:
// - log(message: string): void
// - error(message: string): void
// - warn(message: string): void
Exercise 5: Extending Interfaces
// Create a base interface 'Vehicle' with:
// - make (string)
// - model (string)
// - year (number)
// Then create 'Car' that extends 'Vehicle' with:
// - doors (number)
// - drive(): void
Best Practices
- Use interfaces for object shapes - They're designed for this
- Prefer interfaces over type aliases - For object types, interfaces are more flexible
- Use readonly for immutability - When properties shouldn't change
- Make properties optional when appropriate - Not everything is required
- Extend interfaces for reusability - Build on existing interfaces
Common Mistakes
- Forgetting required properties
// ❌ Bad
interface User {
name: string;
age: number;
}
const user: User = {
name: "Alice"
// Missing age!
};
- Using interfaces for primitives
// ❌ Bad - use type alias instead
interface ID = string;
// ✅ Good
type ID = string;
- Not using readonly when appropriate
// ❌ Bad
interface Config {
apiUrl: string; // Should be readonly
}
// ✅ Good
interface Config {
readonly apiUrl: string;
}
Next Steps
Learn about Classes.