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

如何精简TypeScript泛型函数消除重复代码(无类型断言)

问题:精简Payload转Resource的重复逻辑(无类型断言)

我在梳理代码库时发现,一个根据Payload生成对应Resource的函数存在大量重复逻辑——多组Payload-Resource对的映射逻辑完全一致,但当前函数通过多个条件分支重复实现相同代码。我希望在不使用类型断言(如as AResource)的前提下精简函数,尝试泛型实现时遇到了类型不兼容错误,求解决方案。

当前函数示例

function getResourceFromPayload(
  payload: AResourcePayload,
  asset: Asset
): AResource;
function getResourceFromPayload(
  payload: BResourcePayload,
  asset: Asset
): BResource;
function getResourceFromPayload<T extends AResourcePayload | BResourcePayload>(
  payload: T,
  asset: Asset
): AResource | BResource | undefined {

  if (payload.type === 'A') {
    return {
      ...payload,
      id: payload.id.replace('temp-', 'local-'),
      data: {
        ...payload.data,
        file: {
          id: `${asset.parentId}/${asset.id}`,
          provider: 'someProvider',
        },
      },
    };
  } else if (payload.type === 'B') {
    return {
      ...payload,
      id: payload.id.replace('temp-', 'local-'),
      data: {
        ...payload.data,
        file: {
          id: `${asset.parentId}/${asset.id}`,
          provider: 'someProvider',
        },
      },
    };
  }
  return;
}

尝试的泛型实现及报错

function getResourceFromPayloadNew(
  payload: AResourcePayload,
  asset: Asset
): AResource;
function getResourceFromPayloadNew(
  payload: BResourcePayload,
  asset: Asset
): BResource;
function getResourceFromPayloadNew<T extends AResourcePayload | BResourcePayload, Y extends AResource | BResource = T extends BResourcePayload ? BResource : T extends AResourcePayload ? AResource : never>(
  payload: T,
  asset: Asset
): AResource | BResource | undefined {
  const resource: Y = {
      ...payload,
      id: payload.id.replace('temp-', 'local-'),
      data: {
          ...payload.data,
          file: {
              id: `${asset.parentId}/${asset.id}`,
              provider: 'someProvider',
          },
      },
  };
  return resource;
}

错误信息

类型 'T & { id: string; data: { file: { id: string; provider: string; }; displayName: string; } | { file: { id: string; provider: string; }; source: string; }; }' 无法分配给类型 'Y'。
  'Y' 可以被实例化为任意类型,该类型可能与 'T & { id: string; data: { file: { id: string; provider: string; }; displayName: string; } | { file: { id: string; provider: string; }; source: string; }; }' 无关。

解决方案

方案1:保留函数重载+复用转换逻辑

直接保留原有的函数重载保证类型映射的准确性,同时把重复的转换逻辑合并成一段代码,彻底消除重复:

function getResourceFromPayload(
  payload: AResourcePayload,
  asset: Asset
): AResource;
function getResourceFromPayload(
  payload: BResourcePayload,
  asset: Asset
): BResource;
// 实现部分统一处理所有Payload类型
function getResourceFromPayload(
  payload: AResourcePayload | BResourcePayload,
  asset: Asset
): AResource | BResource {
  // 只写一次转换逻辑,适用于所有Payload-Resource对
  return {
    ...payload,
    id: payload.id.replace('temp-', 'local-'),
    data: {
      ...payload.data,
      file: {
        id: `${asset.parentId}/${asset.id}`,
        provider: 'someProvider',
      },
    },
  };
}

优势:

  • 完全消除重复的条件分支代码,逻辑只实现一次
  • 函数重载保证输入特定Payload时,返回对应明确的Resource类型
  • 无需任何类型断言,TypeScript会自动推导正确类型
  • 后续新增Payload-Resource对时,仅需添加对应重载即可

方案2:泛型映射类型(适合大规模扩展场景)

如果未来需要扩展大量Payload-Resource对,可以定义类型映射关系,用泛型自动推导返回类型:

// 定义Payload与Resource的映射关系
type PayloadResourceMap = {
  A: { payload: AResourcePayload; resource: AResource };
  B: { payload: BResourcePayload; resource: BResource };
};

// 提取所有Payload类型
type AnyPayload = PayloadResourceMap[keyof PayloadResourceMap]['payload'];

// 泛型函数,自动推导返回的Resource类型
function getResourceFromPayload<T extends AnyPayload>(
  payload: T,
  asset: Asset
): Extract<PayloadResourceMap[keyof PayloadResourceMap], { payload: T }>['resource'] {
  return {
    ...payload,
    id: payload.id.replace('temp-', 'local-'),
    data: {
      ...payload.data,
      file: {
        id: `${asset.parentId}/${asset.id}`,
        provider: 'someProvider',
      },
    },
  };
}

优势:

  • 扩展性强,新增类型仅需更新PayloadResourceMap
  • 泛型自动维护Payload与Resource的类型关联

内容的提问来源于stack exchange,提问作者Tomasz Golinski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 05:45:07