使用对象扩展来满足通用联合类型的打字稿

col*_*rtz 5 typescript typescript-generics

type给定一个具有公共字段和的联合类型fedBy。我想编写一个函数,它接受 anAnimal作为类型,第二个参数具有该动物特有的参数。我可以让签名和推理工作,但在如何返回结果方面做错了。

type FeedAnimalParams =
  | {
      type: 'Dog'
      fedBy: string
      bowl: true
    }
  | {
      type: 'Cat'
      fedBy: string
      fish: string
    }
  | {
      type: 'Bird'
      fedBy: string
      seed: string
    }
Run Code Online (Sandbox Code Playgroud)

我正在推断文字类型,提取type属性

type Animal = FeedAnimalParams['type']
//type Animal = "Dog" | "Cat" | "Bird"
Run Code Online (Sandbox Code Playgroud)

我使用通用方法提取每种动物所需的独特字段。

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

type FeedResult<T> = {
  message: string
  result: T
}
Run Code Online (Sandbox Code Playgroud)

这是我要写的函数

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


//other lib
function CantChangeSignature(input:FeedAnimalParams){
  console.log(input)
} 
Run Code Online (Sandbox Code Playgroud)

我在调用该函数时获得智能感知,因此推理似乎有效,但该函数无法编译。


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

feed('Joe', 'Dog', {
  bowl: true,
})
Run Code Online (Sandbox Code Playgroud)

游乐场链接

编辑:1 尝试避免更改返回类型签名,因为我需要将其传递到另一个我无法更改的函数中。

FeedResult<FeedAnimalParams>

//also tried this as the return type

FeedResult<Extract<FeedAnimalParams, {type:T}>>

Run Code Online (Sandbox Code Playgroud)

更新了游乐场

Ada*_*mas 1

您需要将返回类型与泛型相关联,否则 TS 无法使返回值特定于传递的值,animalType因此它认为它同时是所有返回值。

function feed<T extends Animal>(
  byWhom: string,
  animalType: T,
  params: UniqueAnimalParams<T>,
): FeedResult<UniqueAnimalParams<T> & {type: T, fedBy: string}>   {
  return {
    message: `Fed by ${byWhom}`,
    result: {
      type: animalType,
      fedBy: byWhom,
      ...params,
    },
  }
}
Run Code Online (Sandbox Code Playgroud)