如何精简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
相关产品推荐
相关产品推荐

