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

TypeScript中使用对象扩展满足泛型联合类型的编译问题

TypeScript泛型函数返回联合类型编译错误问题

有一个包含共同字段type和fedBy的联合类型FeedAnimalParams,需要编写泛型函数feed:接收投喂者名称、动物类型Animal及对应动物的独有参数。通过泛型提取每种动物的独有参数类型UniqueAnimalParams,并定义返回类型FeedResult。函数调用时类型推断正常,但返回合并type、fedBy和独有参数的对象时出现编译错误,且需将结果传入不可修改的外部函数CantChangeSignature,调整返回类型后仍未解决问题。


联合类型定义

type FeedAnimalParams =
  | {
      type: 'Dog'
      fedBy: string
      bowl: true
    }
  | {
      type: 'Cat'
      fedBy: string
      fish: string
    }
  | {
      type: 'Bird'
      fedBy: string
      seed: string
    }

提取Animal类型

type Animal = FeedAnimalParams['type']
// type Animal = "Dog" | "Cat" | "Bird"

泛型提取独有参数

type UniqueAnimalParams<T extends Animal> = Omit<Extract<FeedAnimalParams, { type: T }>, 'type' | 'fedBy'>

type FeedResult<T> = {
  message: string
  result: T
}

函数实现及编译错误

function feed<T extends Animal>(
  byWhom: string,
  animalType: T,
  params: UniqueAnimalParams<T>,
): FeedResult<FeedAnimalParams> {
  return {
    message: `Fed by ${byWhom}`,
    result: { // 编译错误位置
      type: animalType,
      fedBy: byWhom,
      ...params,
    },
  }
}

// 外部不可修改函数
function CantChangeSignature(input: FeedAnimalParams){
  console.log(input)
} 

函数调用示例

feed('Colin', 'Bird', {
  seed: 'Sunflower',
})

feed('Joe', 'Dog', {
  bowl: true,
})

尝试过的返回类型调整

FeedResult<FeedAnimalParams>

// 也曾尝试此返回类型
FeedResult<Extract<FeedAnimalParams, {type:T}>>

解决方案

问题核心是TypeScript无法自动推断出合并后的对象严格匹配FeedAnimalParams的某个成员,以下两种方案可解决:

方案1:类型断言+精确返回类型

将合并对象断言为对应动物的参数类型,同时把函数返回类型设为精确的子类型(可兼容外部函数):

function feed<T extends Animal>(
  byWhom: string,
  animalType: T,
  params: UniqueAnimalParams<T>,
): FeedResult<Extract<FeedAnimalParams, { type: T }>> {
  return {
    message: `Fed by ${byWhom}`,
    result: {
      type: animalType,
      fedBy: byWhom,
      ...params,
    } as Extract<FeedAnimalParams, { type: T }>,
  }
}

方案2:重构类型映射关系

通过定义动物类型与参数的映射,让TypeScript更清晰地识别类型关联:

// 定义动物类型到独有参数的映射
type AnimalParamMap = {
  Dog: { bowl: true },
  Cat: { fish: string },
  Bird: { seed: string }
}

// 基于映射重构联合类型
type FeedAnimalParams = {
  [K in keyof AnimalParamMap]: {
    type: K,
    fedBy: string
  } & AnimalParamMap[K]
}[keyof AnimalParamMap]

type Animal = keyof AnimalParamMap
type UniqueAnimalParams<T extends Animal> = AnimalParamMap[T]

function feed<T extends Animal>(
  byWhom: string,
  animalType: T,
  params: UniqueAnimalParams<T>,
): FeedResult<FeedAnimalParams> {
  return {
    message: `Fed by ${byWhom}`,
    result: {
      type: animalType,
      fedBy: byWhom,
      ...params,
    } as FeedAnimalParams,
  }
}

兼容性验证

调整后的函数返回值可正常传入外部函数:

const birdResult = feed('Colin', 'Bird', { seed: 'Sunflower' })
CantChangeSignature(birdResult.result) // 无编译错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 06:24:35