讲解

Promise 是异步结果的类型:then 回调拿到 T,await 解包出 T。async 函数的返回值会被自动包成 Promise——function f(): Promise 与 async function f(): number 等价,后者内部 return 1 即可。标注异步函数时写「解包后」的类型(async function loadUser(): Promise,函数体里 return user,不是 return Promise.resolve(user)——虽然那样也合法,Promise 会被展平)。

并行与竞态的组合器都有精确的类型签名:Promise.all 传入元组时返回类型也是元组([Promise, Promise] → [string, number],各自位置保持类型);Promise.allSettled 返回 { status, value/reason } 的判别联合,处理结果时按 status 收窄;Promise.race 返回最快的那个成员类型的联合。串行 await 链类型自然流动,没有额外负担。

错误处理是异步类型的短板:Promise 的 reject 没有类型参数(永远当 unknown 处理),所以「错误类型安全」要靠约定——统一错误模型(如 Result<T, E> 判别联合)是大型项目的常见实践。把「成功/失败」建模进返回类型,比依赖 catch 里的 unknown 更可控。

示例

async/await 的类型标注:

interface User {
  id: number;
  name: string;
}

async function fetchUser(id: number): Promise<User> {
  await new Promise((r) => setTimeout(r, 10)); // 模拟网络
  return { id, name: `用户${id}` };
}

async function main(): Promise<void> {
  const user: User = await fetchUser(1);
  console.log(`加载到:${user.name}`);
}

void main();

Promise.all 的元组精度与并发:

async function fetchTitle(): Promise<string> {
  return "标题";
}
async function fetchViews(): Promise<number> {
  return 1024;
}

async function loadPage(): Promise<string> {
  const [title, views] = await Promise.all([fetchTitle(), fetchViews()]);
  return `《${title}》阅读 ${views}`;
}

void loadPage().then(console.log);

Result 判别联合:把错误变成类型的一部分:

type Result<T, E = string> = { ok: true; value: T } | { ok: false; error: E };

interface User {
  id: number;
  name: string;
}

async function tryFetchUser(id: number): Promise<Result<User>> {
  if (id <= 0) {
    return { ok: false, error: "id 必须为正" };
  }
  return { ok: true, value: { id, name: `用户${id}` } };
}

async function show(id: number): Promise<void> {
  const r = await tryFetchUser(id);
  if (r.ok) {
    console.log(`你好,${r.value.name}`);
  } else {
    console.log(`失败:${r.error}`);
  }
}

void show(1);
void show(-1);

常见坑

  • 给 async 函数返回类型写成 T:async function f(): number 是错的,必须是 Promise——编译器会提示,但初学者常困惑。
  • Promise.all 传数组丢精度:传 Array<Promise<A | B>> 得到 (A | B)[];想要逐位置精确就传字面量元组(直接写 [a, b] 字面量即可,TS 4.9+ 推断已足够聪明)。
  • await 一个非 Promise:await 普通值合法但通常是写错了(忘了调异步函数);no-await-in-promise 类 lint 规则能兜底。
  • 在循环里顺序 await 本可并行的请求:性能问题不是类型问题,但类型签名能暴露——返回 Promise<T[]> 的 map 里记得配 Promise.all。

小结

async 函数返回 Promise,标注写解包后的 T;Promise.all 保元组精度;reject 无类型,严肃项目用 Result 联合建模错误。最后一章:从 JavaScript 迁移与全局最佳实践。