异步编程:Promise
本教程共 80 篇 · 第 73 篇 · 更新于 2026-08-10 · 约 15 分钟阅读
本节目标:搞懂 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 类型的联合