在打字稿中,基于对象形状的类型推断是否可行?

Is type inference based off the shape of an object possible in typescript?

提问人:Sheen 提问时间:10/20/2022 最后编辑:Sheen 更新时间:10/20/2022 访问量:47

问:

假设我有 3 个接口:

interface Animal {
  height: number
  name: string
}

interface Dog extends Animal {
  numberOfTeeth: number
  hasTail: boolean
}

interface Cat extends Animal {
  sizeOfPaw: number
}

假设我查询了一个这样的 api:

function getAnimal(id:string, animalType:string) : Animal {
  const res = await (axios.get(`http://animalapi.com/${animalType}/${id}`));
  return res.data; // Could be a cat, could be a dog, based on animal type
}

如果我尝试这样做:

const animal:Dog = getAnimal("1", "dog"); // Would it be possible to not need to pass the string dog? Perhaps store the "dog" value within the interface and pass it from there?

我收到错误“Animal 缺少以下类型”,然后是 Dog 中的类型。

是否有可能从同一个函数中同时获取 Dog 和 Cat,而不必重复调用?

节点.js Typescript 鸭子类型

评论

0赞 jcalz 10/20/2022
也许是这样?我不确定为什么您的实现似乎返回一个承诺而不是实际值,或者如何处理第三方依赖项,例如 ,所以我只是忽略了这一点。您还在代码注释中编写了看起来像第二个问题的内容。SO 中的问题帖子应该问一个主要问题,所以如果你想回答这个问题,你可能想为它做一个单独的帖子(但简短的回答是否定的,类型系统被删除了,所以如果 JS 代码需要,那么你需要直接传递;运行时没有接口)。axios"dog""dog"
0赞 jcalz 10/20/2022
哦,是和或一个?你能编辑一下来澄清这一点吗?无论如何,如果符合您的需求,我可以写一个答案来解释;如果没有,我错过了什么?idnumberstring
0赞 Sheen 10/20/2022
对于问题的格式错误,我们深表歉意。使用 axios 是因为我正在查询一个具有几个端点的外部 api,但它返回的许多数据结构都包含重复的值。对于这样的函数,这种声明语法是否仍然可行?
0赞 jcalz 10/20/2022
语法只是为了显示类型。你可能想要实际实现它,但这不是一个关于公理的问题,对吧?还是关于承诺与同步?所以你可以做这个这个或任何数量的事情,但这似乎超出了这个问题的范围。真正的问题是关于从输入或到输出或 的打字,对吧?你希望我如何回答这个问题?declare"dog""cat"DogCat
0赞 Sheen 10/20/2022
我相信 ProductionMap 解决方案解决了我的问题,谢谢!

答:

0赞 Aleksandr Hovhannisyan 10/20/2022 #1

在打字稿中,基于对象形状的类型推断是否可行?

是的,这被称为类型缩小,你可以用一个可区分的联合来做到这一点,该联合将 和 形状连接起来,但为每个形状提供一个唯一的“标签”,以将其与其他形状区分开来(这可能是一个属性,如 ,,或任何你想称呼它的东西):DogCatidtype

interface AnimalBase {
  height: number
  name: string
}

interface Dog extends AnimalBase {
  type: 'dog';
  numberOfTeeth: number
  hasTail: boolean
}

interface Cat extends AnimalBase {
  type: 'cat';
  sizeOfPaw: number
}

type Animal = Dog | Cat;

现在,当您创建一个 ,TypeScript 将期望您通过指定属性来识别特定形状。然后,你可以随心所欲地传递,因为它是一个字符串文字:Animaltypeanimal.type

const dog: Animal = {
  type: 'dog',
  numberOfTeeth: 100,
  hasTail: true,
  height: 100,
  name: 'Roofus'
};

getAnimal("1", animal.type)

此模式还有一个额外的好处,即允许您将泛型类型缩小到特定的子类型。例如,您可以使用它来编写类型谓词,以检查动物是否属于特定成分类型:Animal

const isDog = (animal: Animal): animal is Dog => {
  return animal.type === 'dog';
}

const dog: Animal = {
  type: 'dog',
  numberOfTeeth: 100,
  hasTail: true,
  height: 100,
  name: 'Roofus'
};

// here, TypeScript knows that animal is now of type Dog
if (isDog(dog)) {
  // myAnimal shape is now Dog
}

如果没有可区分的并集,你需要做一个类型断言,并检查一个你知道对狗来说绝对独特的属性(也许,但对猫来说也是如此):hasTail

const isDog = (animal: Animal): animal is Dog => {
  return typeof (animal as Dog).numberOfTeeth !== 'undefined';
}