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 包装),实现签名兼容所有重载。

📖 小节

📝 作业

  1. 基础题(难度⭐):写一个 delay(ms: number): Promise<void> 函数,返回 ms 毫秒后 resolve 的 Promise。用 async/await 调用它实现"等待1秒后打印消息"。
  2. 进阶题(难度⭐⭐):写一个 retry<T>(fn: () => Promise<T>, maxRetries: number): Promise<T> 函数——fn 失败时自动重试,最多重试 maxRetries 次。用 try/catch + 循环实现。
  3. 挑战题(难度⭐⭐⭐):实现 concurrentLimit<T>(tasks: (() => Promise<T>)[], limit: number): Promise<T[]>——并发执行 tasks,但最多同时运行 limit 个。所有任务完成后返回结果数组(保持顺序)。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏