-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
24: Practice Exercise: Typing a REST API Response Shape
Wait, do I have to type the whole JSON object, or just the data I'm using?
Technically, you only need to type what you use. If the API sends back fifty fields but you only care about the user's email, you can just define those. But in my experience, that's a trap. When you're working on a professional team, typing the full expected shape of the response serves as documentation for everyone else. It tells the next developer, "This is what the backend actually sends."
Let's say we're hitting a Product Catalog API. Instead of a loose object, I'd define the specific entities first. Notice how I'm splitting the Price into its own type; it makes the code much cleaner than nesting everything in one giant block.
type Price = {
amount: number;
currency: 'USD' | 'EUR' | 'GBP';
};
interface Product {
id: string;
sku: string;
name: string;
description: string;
price: Price;
tags: string[];
}
Most APIs wrap their data in a "response envelope"—how do I handle that without repeating myself?
You'll rarely get a raw array or object back from a real-world API. Usually, you get a wrapper with things like status, page, or totalCount. If you create a new interface for every single endpoint, you're going to end up with a file that's 2,000 lines of redundant code.
The trick here is to use a Generic. I always create a standard ApiResponse type that can wrap any data shape. It's a lifesaver.
interface ApiResponse<T> {
data: T;
meta: {
totalItems: number;
currentPage: number;
totalPages: number;
};
error?: {
code: string;
message: string;
};
}
// Now, when I fetch a list of products, I just do this:
type ProductListResponse = ApiResponse<Product[]>;
// And if I fetch a single product by ID:
type ProductDetailResponse = ApiResponse<Product>;
What do I do when the API is inconsistent and some fields just... disappear?
This is where things get annoying. Some APIs send null, some omit the key entirely, and some send an empty string. To be safe, I lean heavily on optional properties (the ? operator). If a field isn't guaranteed 100% of the time, mark it optional. I'd rather TypeScript force me to check if a value exists now than have the app crash in production because of an undefined error.
Look at the description field in this updated version. Maybe some products don't have one. By marking it optional, TypeScript will remind me to use optional chaining (product.description?.toLowerCase()) when I actually use the data in a component.
interface Product {
id: string;
sku: string;
name: string;
description?: string; // Optional, because the API is flaky
price: Price;
tags: string[];
discountPrice?: Price; // Only exists if the item is on sale
}📋 Practical Task
Exercise: Typing the "Digital Library" API Response
You are integrating a library management API. The API returns a "Book Detail" response that includes the book's core information and a list of authors. Some books are "out of print," in which case the availableDate field is omitted from the response entirely.
Your Task: Create the following type definitions to match this JSON shape:
{
"data": {
"isbn": "978-3-16-148410-0",
"title": "TypeScript Deep Dive",
"publishedYear": 2021,
"availableDate": "2023-12-01",
"authors": [
{ "id": "auth_1", "name": "Jane Doe" },
{ "id": "auth_2", "name": "John Smith" }
]
},
"meta": {
"requestId": "req_abc123",
"timestamp": 1672531200
}
}
Requirements:
- Create an
Authorinterface. - Create a
Bookinterface (ensureavailableDateis optional). - Create a generic
ApiWrapper<T>interface to handle thedataandmetafields. - Create a final type
BookResponsethat combines the wrapper and the book.
There are no comments for now.