【问题标题】:Assumption of Type in Custom Type with Strongly-Typed Properties具有强类型属性的自定义类型中的类型假设
【发布时间】:2019-07-12 17:32:27
【问题描述】:

考虑以下 Typescript 类型...

export type Dog = {
    color: string,
    earsFloppy: boolean,
    _type: "dog"
}

export type Fish = {
    color: string,
    finsFlowy: boolean,
    _type: "fish"
}

export type Pet = Dog | Fish;

export type PetMap = {
    dog: Dog[],
    fish: Fish[]
}

考虑以下函数(也在 Typescript 中)...

var myPetMap = { dog: [], fish: [] };

function addToPetMap(newPet: Pet): void {
    myPetMap[newPet._type].push(newPet);    
}

我的项目中有几乎相同的设置,但出现以下错误:

[ts] Cannot invoke an expression whose type lacks a call signature. Type '((...items: Dog[]) => number) | ((...items: Fish[]) => number)' has no compatible call signatures. [2349]

但是执行以下操作可以解决问题...

var myPetMap = { dog: [], fish: [] };

function addToPetMap(newPet: Pet): void {
    switch(newPet._type) {
        case 'dog':
            myPetMap[newPet._type].push(newPet);
        break;
        case 'fish':
            myPetMap[newPet._type].push(newPet);
        break;
    }
}

就好像newPet._type 的值必须在myPetMap[newPet._type].push(newPet) 存在的范围内是已知的或具体的。它不能存在于newPet._type 的值不能保证为dog (x) 或fish 的范围内。

但是,因为Dog._typetype"dog"Fish._typetype"fish",所以Dog._type的值可以only 为"dog"Fish._type 的值only"fish"。我不明白为什么

myPetMap[newPet._type].push(newPet);

是错误的。

当传入的PetDog时,newPet._type只能是"dog",新的宠物会被添加到myPetMap["dog"],这是一个Dog对象的数组。

当传入的PetFish时,newPet._type只能是"fish",新的宠物会被添加到myPetMap["fish"],这是一个Fish对象的数组。

因为Pet 可能是这两种类型的_type 属性的类型是一个固定值,所以我看不出在Dog 被添加到@ 时会出现任何类型不匹配问题987654354@ 数组,反之亦然。 newPet._type 的值不需要为了将其添加到正确的数组中而变得具体或已知。

有没有办法解决这个问题?任何 Typescript 编译器选项或不同的编写方式?

编辑:

newPet._type 的类型是string

这是我的想法,但尝试更改 newPet._type 的值会产生其自身的错误...

newPet._type = "Something Else";
[ts] Type '"Something Else"' is not assignable to type '"dog" | "fish"'. [2322]
(property) _type: "dog" | "fish"

【问题讨论】:

  • newPet._type的类型是string
  • @Pointy 感谢您的快速回复。我已编辑我的帖子以解决您的评论。
  • 但显然如果你的switch 语句有效,_type 的类型是字符串; switch 语句与 === 逻辑进行比较。
  • @Pointy at runtime _type 是一个字符串,但 typescript 有字符串其他类型来模拟字符串常量。

标签: javascript typescript types


【解决方案1】:

您的方法没有问题,但您确实遇到了类型系统的限制。 Typescript 无法跟踪变量之间的关系,因此 typescript 无法区分您的函数和这个函数之间的区别:

function addToPetMap(_type: "dog" | "fish", newPet: Pet): void {
    myPetMap[_type].push(newPet);    
}

在上面的示例中,可以传入'dog'Fish 的一个实例。虽然在您的情况下这是不可能发生的,因为_type 来自newPet,编译器无法遵循这一点,它只会将newPet._type 的类型视为"dog" | "fish" 并引发错误。

使用开关的解决方法是非常安全的方法。类型断言也是合适的。

【讨论】:

  • 谢谢你的回答,提香。有没有像 switch 语句一样安全但没有明文冗余的解决方案?如果我想支持 100 种不同类型的 Pet,switch 语句会非常大。
  • @Zulfe 据我所知。 jcalz 在 github 上有一个与此类似的场景的问题,它仍然是开放的,所以我怀疑是否有更好的解决方案。
猜你喜欢
  • 1970-01-01
  • 2010-10-12
  • 1970-01-01
  • 2018-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-08
  • 2013-11-22
相关资源
最近更新 更多