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

如何用TypeScript泛型优雅转换扩展Profile的对象?

优化方案

你的实现其实不是反模式,但确实可以更简洁。核心思路是利用Omit工具类型移除原有image属性,再与新的image属性类型交叉,就能在保留所有额外属性的同时替换目标属性的类型:

type Profile = {
  firstName: string
  lastName: string 
  image: string // 相对URL路径
}

type ImageEnhanced = {
  src: string 
  width: number 
  height: number
}

// 简化后的转换类型定义
type TransformProfile<T extends Profile> = Omit<T, 'image'> & { image: ImageEnhanced }

// 模拟获取图片信息的异步函数
async function getImage(src: string): Promise<ImageEnhanced> {
    return Promise.resolve({
        src: '/some/image.png',
        width: 200,
        height: 150
    })
}

async function mapProfile<T extends Profile>(profile: T): Promise<TransformProfile<T>> {
  const enhancedImage = await getImage(profile.image)  
  return {
    ...profile,
    image: enhancedImage
  }
}

// 使用示例
(async function(){
    const profileWithAddress: Profile & { address: string } = {
        firstName: 'John',
        lastName: 'Doe',
        image: '/some/image.png',
        address: '123 Street' 
    }

    const enhancedProfile = await mapProfile(profileWithAddress)
    type OutputType = keyof typeof enhancedProfile // "image" | "firstName" | "lastName" | "address"
})()

为什么这个写法更优

  1. 语义清晰:Omit<T, 'image'>明确表示移除原类型中的image属性,再交叉{ image: ImageEnhanced }表示替换为新的类型,逻辑一目了然。
  2. 代码简洁:相比原来拆分Pick和Omit再分别映射的写法,减少了冗余的类型映射逻辑,符合TypeScript工具类型的常规用法。
  3. 类型安全一致:最终实现的类型效果和你原来的写法完全一致,同样能正确保留所有扩展属性,同时确保image属性的类型被正确替换。

你的原始实现本质上是手动复刻了Omit+交叉的逻辑,虽然功能正常,但利用内置工具类型可以让代码更简洁易读。

内容的提问来源于stack exchange,提问作者Zach Gollwitzer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 21:45:17