首页 / TypeScript 入门教程 / 异步编程:Promise

TypeScript 入门教程

异步编程:Promise

本教程共 80 篇 · 第 73 篇 · 更新于 2026-08-10 · 约 15 分钟阅读

TypeScriptTypeScript 入门教程Promise异步编程Promise.allPromise.allSettled

本节目标:搞懂 TypeScript 怎么给 Promise 做类型标注——Promise<T>T 如何自动推断、.then() 链式调用时类型怎么传递、Promise.all 等组合方法的返回值类型推导。

Promise<T> 泛型

Promise 是泛型类,T 表示异步操作完成后产出的值的类型:

// 一个返回 number 的 Promise
const numPromise: Promise<number> = Promise.resolve(42);

// 一个返回 string 的 Promise
const strPromise: Promise<string> = Promise.resolve("hello");

大部分场景下你不需要显式写 Promise<number>,TypeScript 会根据 resolve 的值自动推断:

const p1 = Promise.resolve(42);     // Promise<number>
const p2 = Promise.resolve("hi");   // Promise<string>
const p3 = Promise.resolve(true);   // Promise<boolean>

Promise 构造函数里的类型

new Promise(...) 时,TS 通过构造函数参数里的 resolve 调用推断泛型:

const p = new Promise((resolve) => {
  setTimeout(() => {
    resolve("done"); // TS 推断 resolve 是 (value: string) => void
  }, 1000);
});
// p 的类型是 Promise<string>

如果你调 resolve 时传了不同类型的值,TS 会推断出联合类型:

const p = new Promise((resolve) => {
  if (Math.random() > 0.5) {
    resolve(200);      // number
  } else {
    resolve("error");  // string
  }
});
// p 的类型是 Promise<string | number>

手动标注 Promise 构造函数的类型

少数场景下类型推断不够精确,可以用类型参数明确指定:

const p = new Promise<number>((resolve, reject) => {
  const result = calculateSomething();
  if (result !== null) {
    resolve(result);
  } else {
    reject(new Error("计算失败"));
  }
});

注意,reject 的类型不参与 Promise<T>T 推断。reject 总是接受 any 类型的参数(或者说,拒绝理由不体现在 Promise 的泛型参数里)。

.then() 的类型传递

.then() 的回调里返回的类型,会被自动包装进新的 Promise:

Promise.resolve(42)
  .then((num) => {
    // num 类型:number
    return num.toString(); // 返回 string
  })
  .then((str) => {
    // str 类型:string
    console.log(str.toUpperCase());
    return str.length;    // 返回 number
  })
  .then((len) => {
    // len 类型:number
    console.log(len);
  });

每一步的类型推断都很自然:第一个 then 收到 number,返回 string;第二个 then 收到 string,返回 number;第三个 then 收到 number

在 .then() 里直接返回 Promise

如果你在 .then() 里返回另一个 Promise,TS 不会把它包成 Promise<Promise<T>>,而是自动”展平”:

Promise.resolve(1)
  .then((n) => {
    return Promise.resolve(n * 2); // 返回 Promise<number>
  })
  .then((n) => {
    // n 类型:number(不是 Promise<number>)
    console.log(n); // 2
  });

这是 JS 本身就有的 Promise 扁平化行为,TS 的类型系统忠实反映了这一点。

.catch() 的类型

.catch() 的返回值也是 Promise<T>,也就是说 catch 里可以”修复”错误并继续链:

Promise.resolve("hello")
  .then((str) => {
    throw new Error("出错了");
  })
  .catch((err) => {
    // err 类型:any(默认,strict 下也如此)
    console.error(err.message);
    return "fallback";  // 返回 string
  })
  .then((result) => {
    // result 类型:string(来自 then 的 "hello" 或 catch 的 "fallback")
    console.log(result);
  });
Note

.catch()err 参数类型在 TS 里永远是 any(或 unknown,取决于 useUnknownInCatchVariables 配置),不会从 reject 的值推断。实际运行时 catch 可以捕获任何东西——不一定是 Error 实例,所以 TS 保持保守。

Promise 组合方法的类型推断

Promise.all

Promise.all 接受一个 Promise 数组,返回一个 Promise,类型推导可以说是 TS 中最精妙的部分之一:

const [name, age, active] = await Promise.all([
  Promise.resolve("Alice"),   // Promise<string>
  Promise.resolve(25),        // Promise<number>
  Promise.resolve(true),      // Promise<boolean>
]);

// name: string, age: number, active: boolean
// 返回类型:Promise<[string, number, boolean]>

每个元素的类型精确推断,组合成元组。如果传入的是变量数组(而非内联数组字面量),推断会不同:

const promises = [Promise.resolve("Alice"), Promise.resolve(25), Promise.resolve(true)];
// promises 类型:Promise<string | number | boolean>[]

const results = await Promise.all(promises);
// results 类型:(string | number | boolean)[]

因为数组字面量被推断成了 (string | number | boolean)[],TS 丢失了元组的精确位置信息。想保留精确类型?用 as const

const promises = [
  Promise.resolve("Alice"),
  Promise.resolve(25),
  Promise.resolve(true),
] as const;

const results = await Promise.all(promises);
// results 类型:readonly [string, number, boolean]

Promise.allSettled

allSettled 不关心成败,每个元素都返回 PromiseSettledResult

const results = await Promise.allSettled([
  Promise.resolve(42),
  Promise.reject(new Error("fail")),
]);

// results 类型:
// [PromiseSettledResult<number>, PromiseSettledResult<never>]

results.forEach((result) => {
  if (result.status === "fulfilled") {
    console.log(result.value);  // value 类型:number(第一个元素)
  } else {
    console.log(result.reason); // reason 类型:any
  }
});

PromiseSettledResult<T> 是联合类型:

type PromiseSettledResult<T> =
  | { status: "fulfilled"; value: T }
  | { status: "rejected"; reason: any };

通过 status 收窄后,value 的类型会精确到对应 Promise 的 T

Promise.race

race 返回最先完成的那个 Promise 的结果,类型是传入的所有 Promise 的联合:

const result = await Promise.race([
  fetchUser(),     // Promise<User>
  timeout(5000),   // Promise<never>(超时抛错)
]);

// result 类型:User

如果 timeout 返回 Promise<never>,那被排除出联合类型(User | never = User)。如果超时返回的是 Promise<Error>,那 result 类型就是 User | Error

Promise.any

race 类似,但忽略 rejection,只等第一个 fulfilled。类型推断也类似:

const result = await Promise.any([
  fetch("./cdn1.json"),   // Promise<Data>
  fetch("./cdn2.json"),   // Promise<Data>
]);

// result 类型:Data
Tip

如果所有 Promise 都被 reject,Promise.any 会抛出 AggregateError,TS 对此没有额外的类型检查——你需要自己处理。

异步函数的返回类型本质上就是 Promise

这一点在下一章会详细展开,但这里先提一句:任何标注了 async 的函数,返回类型都是 Promise,即使你 return 一个原始值:

async function getNumber(): Promise<number> {
  return 42; // 42 自动被包装成 Promise.resolve(42)
}

不写 Promise<number> 也可以,TS 会推断返回类型为 Promise<number>。但显式标注能让意图更清晰、方便文档生成。

小结

  • Promise<T>T 表示 resolve 值的类型,TS 在绝大多数场景能自动推断
  • .then() 的链式调用中,每一步回调的返回类型会传给下一步的参数类型
  • .then() 里返回 Promise 会自动展平,不会出现 Promise<Promise<T>>
  • .catch()err 参数类型固定为 any(或 unknown),不参与泛型推断
  • Promise.all 对元组有精确的类型推导,但对普通数组会退化为联合类型
  • Promise.allSettled 返回 PromiseSettledResult<T>,通过 status 收窄可拿到精确类型
  • Promise.race / Promise.any 返回传入 Promise 类型的联合