TypeScript: TypeScript 异步编程与类型
最后更新:2026-08-26
异步编程是 JavaScript 的核心模式——TypeScript 通过泛型 Promise 和 async/await 为异步操作添加了完整的类型支持。
1. Promise 类型基础
(1) Promise 的泛型类型
Promise 是泛型类型——Promise<T> 表示"将来会产生一个 T 类型的值":
TYPESCRIPT
// 同步函数——直接返回值
function getUserSync(): string {
return "Charlie";
}
// 异步函数——返回 Promise<string>
function getUserAsync(): Promise<string> {
return Promise.resolve("Charlie");
}
// 模拟网络请求
function fetchUser(id: number): Promise<{ id: number; name: string }> {
return new Promise((resolve) => {
setTimeout(() => resolve({ id, name: "用户" + id }), 100);
});
}
(2) then/catch 的类型推导
TYPESCRIPT
let promise: Promise<string> = Promise.resolve("hello");
promise
.then(value => {
// value 类型自动推断为 string
console.log(value.toUpperCase()); // ✅
return value.length; // 返回 number → then 链变为 Promise<number>
})
.then(length => {
// length 类型自动推断为 number
console.log(length.toFixed(2)); // ✅
})
.catch(error => {
// error 类型为 any(TypeScript 无法推断错误类型)
console.log(error.message);
});
(3) Promise 类型的不变性与安全
TYPESCRIPT
// Promise 是协变的——Promise<string> 可以赋给 Promise<string | number>
let p1: Promise<string> = Promise.resolve("hello");
let p2: Promise<string | number> = p1; // ✅ 协变安全
// 反过来不行
// let p3: Promise<string> = p2; // ❌ Promise<string | number> 不能赋给 Promise<string>
▶ 示例:类型安全的 API 客户端
TYPESCRIPT
interface ApiResponse<T> {
status: number;
data: T;
message: string;
}
interface User {
id: number;
name: string;
email: string;
}
interface Product {
id: number;
title: string;
price: number;
}
function apiGet<T>(url: string): Promise<ApiResponse<T>> {
// 模拟 API 调用
return new Promise((resolve) => {
setTimeout(() => {
resolve({
status: 200,
data: {} as T, // 实际项目中由 JSON.parse 结果断言
message: "OK"
});
}, 100);
});
}
// 使用——每种请求有精确的返回类型
async function getUser(id: number): Promise<User> {
let response = await apiGet<User>(`/api/users/${id}`);
return response.data;
}
async function getProducts(): Promise<Product[]> {
let response = await apiGet<Product[]>("/api/products");
return response.data;
}
2. async/await 的类型
(1) async 函数的返回值
async 函数总是返回 Promise——即使你返回一个普通值,TypeScript 会自动包装:
TYPESCRIPT
// 显式返回值——自动包装为 Promise
async function greet(): Promise<string> {
return "你好"; // 等价于 return Promise.resolve("你好")
}
// 显式返回 Promise——不会双重包装
async function fetchName(): Promise<string> {
return Promise.resolve("Charlie"); // 不会变成 Promise<Promise<string>>
}
(2) await 的类型推导
await 解包 Promise——await Promise<T> 的类型是 T:
TYPESCRIPT
async function example() {
let name: string = await Promise.resolve("Charlie"); // ✅ 解包为 string
let count: number = await Promise.resolve(42); // ✅ 解包为 number
let user: User = await fetchUser(1); // ✅ 解包为 User
}
(3) await 的陷阱——可能为 rejected
TYPESCRIPT
async function riskyOperation(): Promise<number> {
// 这个 Promise 可能 reject
let result = await mayFail(); // result 类型是 number——但运行时可能抛异常
return result;
}
async function mayFail(): Promise<number> {
if (Math.random() > 0.5) {
throw new Error("随机失败");
}
return 42;
}
// 安全写法——用 try/catch
async function safeOperation(): Promise<number | null> {
try {
let result = await mayFail();
return result;
} catch {
return null;
}
}
3. Promise 的工具类型
(1) Awaited——解包 Promise 类型
TypeScript 4.5 内置了 Awaited<T> 类型——递归解包 Promise:
TYPESCRIPT
type A = Awaited<Promise<string>>; // string
type B = Awaited<Promise<Promise<number>>>; // number(递归解包)
type C = Awaited<string>; // string(非 Promise 直接返回)
type D = Awaited<Promise<string | number>>; // string | number
(2) 实际用途——提取异步函数返回类型
TYPESCRIPT
async function fetchUser(id: number) {
let response = await fetch(`/api/users/${id}`);
return response.json() as Promise<{ id: number; name: string }>;
}
// 提取异步函数的返回值类型(去掉 Promise 包装)
type UserResponse = Awaited<ReturnType<typeof fetchUser>>;
// { id: number; name: string }
(3) 自定义 Promise 工具类型
TYPESCRIPT
// 提取 Promise 数组中每个 Promise 解析后的类型
type UnwrapPromiseArray<T> = {
[K in keyof T]: T[K] extends Promise<infer U> ? U : T[K];
};
type Input = [Promise<string>, Promise<number>, boolean];
type Output = UnwrapPromiseArray<Input>;
// [string, number, boolean]
4. 并发控制的类型安全
(1) Promise.all
TYPESCRIPT
async function loadDashboard() {
// Promise.all 接受不同类型的 Promise 数组
let [users, products, stats] = await Promise.all([
fetchUsers(), // Promise<User[]>
fetchProducts(), // Promise<Product[]>
fetchStats() // Promise<Stats>
]);
// 每个变量有独立的类型
console.log(users.length); // User[].length
console.log(products[0].title); // Product.title
console.log(stats.totalUsers); // Stats.totalUsers
}
(2) Promise.allSettled
TYPESCRIPT
type SettledResult<T> = {
status: "fulfilled";
value: T;
} | {
status: "rejected";
reason: unknown;
};
async function tryMultipleApis() {
let results = await Promise.allSettled([
fetchFromApi1(), // Promise<User>
fetchFromApi2(), // Promise<User>
]);
for (let result of results) {
if (result.status === "fulfilled") {
console.log(result.value.name); // ✅ value 类型为 User
} else {
console.log(result.reason); // unknown
}
}
}
(3) Promise.race 与 Promise.any
TYPESCRIPT
// race——取最先完成的结果(无论成功或失败)
async function fetchWithTimeout(url: string, ms: number) {
let result = await Promise.race([
fetch(url),
new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error("超时")), ms)
)
]);
return result;
}
// any——取最先成功的结果(忽略失败)
async function fetchWithFallback() {
let result = await Promise.any([
fetchFromPrimary(), // 优先
fetchFromSecondary(), // 备用
]);
// result 类型取所有 Promise 的解析值联合类型
}
▶ 示例:并发数据加载器
TYPESCRIPT
interface User { id: number; name: string; }
interface Post { id: number; title: string; authorId: number; }
interface Comment { id: number; postId: number; text: string; }
async function fetchAllData(userId: number) {
let [user, posts, comments] = await Promise.all([
fetchUser(userId),
fetchPosts(userId),
fetchComments(userId)
]);
return { user, posts, comments };
}
async function fetchUser(id: number): Promise<User> {
return { id, name: "用户" + id };
}
async function fetchPosts(userId: number): Promise<Post[]> {
return [{ id: 1, title: "文章1", authorId: userId }];
}
async function fetchComments(userId: number): Promise<Comment[]> {
return [{ id: 1, postId: 1, text: "好文章!" }];
}
async function main() {
let data = await fetchAllData(1);
console.log(`用户:${data.user.name}`);
console.log(`文章数:${data.posts.length}`);
console.log(`评论数:${data.comments.length}`);
}
main();
5. 异步迭代器
(1) AsyncIterable 与 for await...of
TYPESCRIPT
async function* asyncCounter(max: number): AsyncGenerator<number> {
for (let i = 0; i < max; i++) {
await new Promise(resolve => setTimeout(resolve, 100));
yield i;
}
}
async function run() {
for await (let num of asyncCounter(3)) {
console.log(num);
}
// 0 → 1 → 2(每个间隔100ms)
}
(2) 异步生成器的类型
TYPESCRIPT
interface AsyncGenerator<T> {
next(): Promise<IteratorResult<T>>;
return(value?: any): Promise<IteratorResult<T>>;
throw(e?: any): Promise<IteratorResult<T>>;
[Symbol.asyncIterator](): AsyncGenerator<T>;
}
❓ 常见问题
Q async 函数返回值必须写 Promise 吗?
A 建议显式标注。TypeScript 能推断 async 函数返回 Promise,但显式标注
async function fn(): Promise<T> 更清晰——文档作用 + 编译时捕获错误。小函数可以省略返回类型,复杂函数建议标注。Q Promise 的 catch 中 error 类型为什么是 unknown?
A 因为 JavaScript 允许 throw 任何值(不限于 Error),TypeScript 无法保证 catch 到的一定是 Error 类型。建议在 catch 中做类型检查:
if (error instanceof Error) 或用类型断言 error as Error。Q Promise.all 中的一个失败会怎样?
A Promise.all 是"全部成功或全部失败"——任何一个 Promise reject,整个 all 就 reject。如果需要"全部完成(无论成功失败)",用 Promise.allSettled。如果需要"第一个成功",用 Promise.any。
Q 异步函数能重载吗?
A 可以。async 函数的重载和同步函数完全相同——重载签名描述不同参数组合的返回类型(Promise 包装),实现签名兼容所有重载。
📖 小节
Promise<T>是异步操作的类型——T 是将来产生的值类型- async 函数总是返回 Promise,await 解包 Promise 得到 T
Awaited<T>递归解包 Promise 类型——提取异步函数的返回值类型- Promise.all 并发执行多个 Promise,返回元组类型(每个位置独立类型)
- Promise.allSettled 等全部完成,Promise.race 取最快,Promise.any 取最早成功
- 异步生成器
AsyncGenerator<T>配合for await...of实现异步迭代
📝 作业
- 基础题(难度⭐):写一个
delay(ms: number): Promise<void>函数,返回 ms 毫秒后 resolve 的 Promise。用 async/await 调用它实现"等待1秒后打印消息"。 - 进阶题(难度⭐⭐):写一个
retry<T>(fn: () => Promise<T>, maxRetries: number): Promise<T>函数——fn 失败时自动重试,最多重试 maxRetries 次。用 try/catch + 循环实现。 - 挑战题(难度⭐⭐⭐):实现
concurrentLimit<T>(tasks: (() => Promise<T>)[], limit: number): Promise<T[]>——并发执行 tasks,但最多同时运行 limit 个。所有任务完成后返回结果数组(保持顺序)。