You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中Promise.all封装函数丢失可选属性undefined类型问题

优化allPromiseMap函数的类型定义问题

我封装了一个基于Promise.all的函数allPromiseMap,它接收值为Promise的对象,返回键相同、值为对应Promise解析结果的对象。测试时发现一个问题:返回对象的可选属性(比如示例里的world)仍保留可选标记,但把该属性赋值给变量后,类型里的undefined被移除了;而直接定义的同类型对象不会有这个问题。请问该怎么优化函数的类型定义来避免这个问题?

相关代码示例

const allPromiseMap = async <T extends { [KEY in string]: unknown }>(map: {
  [KEY in keyof T]: Promise<T[KEY]>;
}): Promise<T> => {
  // 函数实现逻辑
  const entries = await Promise.all(
    Object.entries(map).map(async ([key, promise]) => [key, await promise])
  );
  return Object.fromEntries(entries) as T;
};

(async () => {
  const promises: {
    hello: Promise<string>;
    world?: Promise<number>;
  } = {
    hello: new Promise(() => { })
  }

  const responseMap = await allPromiseMap(promises);
  const worldResponse = responseMap.world;
  // 此处worldResponse的类型被推断为number,而非预期的number | undefined
})();

问题原因

原来的泛型约束T extends { [KEY in string]: unknown }会让TypeScript在推断T时,将可选属性的Promise<number>解析为number,但没有正确关联可选属性本身携带的undefined可能性。当访问该属性并赋值给变量时,TypeScript错误地移除了undefined类型分支。

解决方案

修改函数的泛型定义,直接基于输入的Promise对象类型进行映射,明确保留可选属性的undefined类型:

// 定义工具类型,提取Promise的解析值类型
type ResolvePromise<T> = T extends Promise<infer U> ? U : never;

// 辅助类型,定义函数返回值类型
type PromiseReturnType<P> = {
  [K in keyof P]: P[K] extends Promise<infer U> ? U : undefined;
};

const allPromiseMap = async <P extends Record<string, Promise<unknown> | undefined>>(
  map: P
): Promise<PromiseReturnType<P>> => {
  const entries = await Promise.all(
    Object.entries(map).map(async ([key, promise]) => [
      key,
      // 处理可选属性可能为undefined的情况
      promise instanceof Promise ? await promise : undefined
    ])
  );
  return Object.fromEntries(entries) as PromiseReturnType<P>;
};

测试验证

修改后,重新测试代码:

(async () => {
  const promises: {
    hello: Promise<string>;
    world?: Promise<number>;
  } = {
    hello: new Promise(resolve => resolve("hi"))
  }

  const responseMap = await allPromiseMap(promises);
  const worldResponse = responseMap.world;
  // 此时worldResponse的类型为number | undefined,符合预期
})();

这样修改后,返回对象的可选属性赋值给变量时,undefined类型会被正确保留,与直接定义同类型对象的行为一致。

内容的提问来源于stack exchange,提问作者Sandy Gifford

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 11:31:20