为什么我会得到一个错误TS2322?如何处理这种情况来摆脱它?
事实上,“makeAnimal”函数不太具体,而“makeCat”更具体,据我所知,Typescript告诉我,我不能为更具体的函数使用不太具体的输出。
但是我需要一个公共函数和一堆更具体的函数来使用它。
请帮我弥补这一切。
const ANIMAL_CAT = 'cat'
const ANIMAL_DOG = 'dog'
type ICat = 'isCat'
type IDog = 'isDog'
type IAnimal = ICat | IDog
type IMakeAnimal = (type: string) => IAnimal
type IMakeCat = () => ICat
type IMakeDog = () => IDog
const makeAnimal: IMakeAnimal = (type) => {
switch (type) {
case ANIMAL_CAT:
return 'isCat'
case ANIMAL_DOG:
default:
return 'isDog'
}
}
// TS2322: Type 'IAnimal' is not assignable to type '"isCat"'.
// Type '"isDog"' is not assignable to type '"isCat"'
const makeCat: IMakeCat = () => makeAnimal(ANIMAL_CAT)
1条答案
按热度按时间b1zrtrql1#
您的
makeAnimal
版本的返回类型只是IAnimal
,它可以是ICat
或IDog
。编译器不会尝试模拟如果您调用makeAnimal(ANIMAL_CAT)
来进一步缩小返回类型会发生什么。您注解了makeAnimal
是一个IMakeAnimal
,其返回类型是IAnimal
,所以这就是编译器看到的。如果你想让函数保持原样,那么你必须通过 * 告诉 * 编译器
makeAnimal(ANIMAL_CAT)
返回的值是ICat
来把丢失的信息添加回去,你可以通过类型Assert来完成:这是可行的,但它之所以安全,是因为你的Assert碰巧是正确的,编译器无法判断你是否在这里犯了错误;它依赖于你的Assert是否准确。也就是说,编译器不是在验证类型安全,而是你在验证类型安全。举个例子:
这也是没有错误的编译,但是你声称
makeAnimal(ANIMAL_DOG)
将返回一个ICat
,编译器只是相信你。如果你想要更多的编译器验证的类型安全性,你需要重构
makeAnimal()
,使它的返回类型依赖于它的输入,比如把它变成一个generic函数...你还需要修改实现,使编译器能够理解你的逻辑匹配泛型返回类型。这里我创建了一个
AnimalMap
Map接口,它表示ANIMAL_CAT
/ANIMAL_DOG
字符串类型和对应的ICat
/IDog
输出类型之间的关系,它只是一个接口,因为字符串类型可以用作对象的键类型。makeAnimal
的类型现在是泛型函数,其中type
参数是泛型类型K
,该泛型类型被约束为keyof AnimalMap
,即...ANIMAL_CAT
或ANIMAL_DOG
。并且makeAnimal
的返回类型是indexed access typeAnimalMap[K]
。表示键类型为K
的AnimalMap
的属性的值类型。调用签名本质上描述了对象属性的查找。事实上,我已经将
makeAnimal
的实现从switch
/case
语句重构为对象属性查找,编译器对这种实现很满意,因为它认为对象可以赋值给AnimalMap
,键type
可以赋值给K
。因此返回类型被视为可赋值给AnimalMap[K]
,如果将其保留为switch
/case
,您会发现编译器无法遵循逻辑,因为K
不一定通过检查type
而改变。无论如何,既然
makeAnimal()
是泛型,返回类型将取决于输入类型,因此makeAnimal(ANIMAL_CAT)
将使编译器推断ANIMAL_CAT
为K
,并且索引访问类型AnimalMap[ANIMAL_CAT]
为ICat
,所以返回类型是ICat
,() => makeAnimal(ANIMAL_CAT)
可以赋值给IMakeCat
,如所期望的:Playground代码链接