Lesson
You've seen basic types. Now let's define your own complex types. TypeScript gives you two main tools: interfaces and type aliases. Think of interfaces as contracts, formal agreements about what an object must contain. Type aliases are more like nicknames, they can describe anything from simple primitives to complex type transformations.
Defining object shapes with interfaces
Interfaces are the most common way to define what an object should look like.
interface User {
id: number;
name: string;
email: string;
age?: number; // Optional property
readonly createdAt: Date; // Can't be modified after creation
}
const user: User = {
id: 1,
name: 'John Doe',
email: 'john@example.com',
createdAt: new Date()
};Extending interfaces
Interfaces can extend each other, creating hierarchies:
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
bark(): void;
}
const myDog: Dog = {
name: 'Rex',
age: 3,
breed: 'Golden Retriever',
bark: () => console.log('Woof!')
};02
Type aliases: The flexible alternative
Type aliases use the type keyword and can represent much more than just object shapes.
// Object shape (similar to interface)
type Point = { x: number; y: number };
// Union type - value can be one of several types
type Status = 'pending' | 'active' | 'inactive';
// Tuple type
type Coordinates = [number, number];
// Function type
type Callback = (error: Error | null, data: string) => void;Intersection types
While interfaces use extends, type aliases use & to combine types:
type Person = { name: string; age: number };
type Employee = { employeeId: number; department: string };
type EmployeePerson = Person & Employee;
// Has all properties of both types03
Interface vs type aliasWhat is type alias?A name given to any TypeScript type using the type keyword, including unions, objects, and complex shapes.
| Feature | Interface | Type Alias |
|---|---|---|
| Object shapes | Primary use case | Works fine |
| Extending | extends keyword | Via intersection & |
| Declaration merging | Supported | Not supported |
| Union types | Not possible | Primary use case |
| Primitives & tuples | Not possible | Supported |
04
Utility types: Transform existing types
TypeScript provides built-in utility types that create variations of a base type.
interface User {
id: number;
name: string;
email: string;
password: string;
}
type PartialUser = Partial<User>; // All properties optional
type UserSummary = Pick<User, 'id' | 'name'>; // Only id and name
type PublicUser = Omit<User, 'password'>; // Everything except password
type ReadonlyUser = Readonly<User>; // All properties readonly| Utility | What it does | Example |
|---|---|---|
Partial<T> | All properties optional | Partial<User> |
Required<T> | All properties required | Required<Config> |
Pick<T, K> | Keep only specified keys | Pick<User, 'id' \| 'name'> |
Omit<T, K> | Remove specified keys | Omit<User, 'password'> |
Readonly<T> | All properties readonly | Readonly<Config> |
Record<K, V> | Object with keys K and values V | Record<string, number> |
05
Quick reference
| Concept | Use interface | Use type alias |
|---|---|---|
| Object shapes for APIs | Yes, supports extension and merging | Fine, but no merging |
Union types (A \| B) | Not possible | Yes, only way |
| Tuple types | Not possible | Yes, only way |
| Composing from pieces | extends keyword | & intersection |
| Transforming types | Via utility types | Via utility types |
AI pitfall, everything optional. AI tools love marking properties as optional (
?) to avoid type errors. If ChatGPT generates an interface where every single field is optional, your type provides almost no safety, any empty object {} satisfies it. Review each property and ask: "Can this legitimately be missing?" Required properties should stay required.