TypeScript
Completed
-
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
100: Typing Promises and Async Functions
Imagine you're at a busy deli. You order a custom sandwich, but the staff doesn't hand you the food immediately. Instead, they give you one of those little plastic buzzers. That buzzer isn't a sandwich—you can't eat it—but it is a promise that a sandwich will exist in the future. There are only two ways this ends: either the buzzer goes off and you get your sandwich (success), or the staff comes over and tells you they've run out of sourdough (failure).
Mapping the Deli to the Code
In TypeScript, a Promise<T> is exactly like that buzzer. The T represents the type of "sandwich" you're expecting back. If you're fetching a user profile from a database, you aren't getting the user immediately; you're getting a Promise<User>.
interface User {
id: string;
username: string;
email: string;
}
// This function doesn't return a User.
// It returns a "buzzer" that will eventually resolve into a User.
async function fetchUserProfile(userId: string): Promise<User> {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
Notice the Promise<User> return type. This tells anyone using this function, "Hey, you can't use the user data right this second, but I guarantee that whenever this finishes, you'll have a User object."
The async Shortcut
I've seen a lot of developers struggle with where to put the type when using the async keyword. Here is the golden rule: any function marked async must return a Promise. Even if you return a simple string, TypeScript wraps it in a Promise automatically.
// Even though I'm returning a string, the return type is Promise<string>
async function getWelcomeMessage(): Promise<string> {
return "Welcome back to the app!";
}
If you try to set the return type as just string on an async function, TypeScript will throw an error. It's reminding you that asynchronous code doesn't happen instantly—it's always a "buzzer" situation.
When the "Sourdough" Runs Out
Now, what happens when the API call fails? In the deli analogy, this is the staff telling you they're out of bread. In TypeScript, this happens in the catch block. One quirk I want you to watch out for: TypeScript doesn't let you specify the type of a caught error. By default, the error is any or unknown.
Because a server could throw a 404, a 500, or a network timeout, TS can't possibly know exactly what the error object looks like. I usually handle this with a type guard or a simple cast if I'm certain of the error shape.
async function loadDashboard() {
try {
const user = await fetchUserProfile('123');
console.log(user.username);
} catch (err) {
// 'err' is unknown. We have to be careful here.
if (err instanceof Error) {
console.error("Failed to load: " + err.message);
} else {
console.error("An unexpected error occurred");
}
}
}
I highly recommend using instanceof Error. It's the cleanest way to tell TypeScript, "I've checked, and this is a standard JavaScript Error object," allowing you to access .message without the compiler complaining.
📋 Practical Task
Implementing a GitHub Repo Stats Fetcher
Your goal is to create a type-safe function that fetches public repository data from the GitHub API. You'll need to define the shape of the API response and ensure the async function is typed correctly.
Requirements:
- Define an interface
RepoStatswithname(string),stargazers_count(number), andforks_count(number). - Write an
asyncfunction calledgetRepoStatsthat takes arepoName(string) and returns aPromise<RepoStats>. - Use
fetch()to get the data fromhttps://api.github.com/repos/[repoName]. - Implement a
try/catchblock that handles potential errors using aninstanceof Errorcheck.
// Start your code here:
interface RepoStats {
// ...
}
async function getRepoStats(repoName: string): Promise<RepoStats> {
// ...
}There are no comments for now.