【发布时间】:2022-01-07 08:45:17
【问题描述】:
考虑以下类型和子类型:
interface Animal {
type: string;
}
interface Dog extends Animal {
type: 'dog';
sound: 'bark';
}
interface Cat extends Animal {
type: 'cat';
purpose: 'world_domination';
}
还有一种方法可以从 type 的值中获取 Animal 子类型:
type Animals = Dog | Cat;
type AnimalFromType<T extends Animals['type']> = Extract<Animals, { type: T }>;
现在假设我有一个动物数组,我想创建 一个返回用户定义类型保护的函数,然后可以在 Array.filter() 调用中使用它:
function createFilter<T extends 'dog' | 'cat'>(type: T): (x: any) => x is AnimalFromType<T> {
return x => x.type === type;
}
问题是函数似乎不可能返回类型保护函数 - 编译器向 TS2322 抱怨 (x: any) => boolean 不能分配给 (x: any) => x is AnimalFromType<T> - 签名必须是类型谓词。出于某种原因,编译器不理解从 lambda 返回的布尔表达式与外部函数签名的 x is AnimalFromType<T> 部分匹配。
这是为什么呢?什么会阻止编译器理解 createFilter('dog') 返回一个用户定义的类型保护(它接受一个对象并返回 x is Dog)?
编辑:@jcalz 在此处提供的完整 MRE:https://tsplay.dev/wX219m
【问题讨论】:
-
请提供minimal reproducible example,清楚地表明您面临的问题。理想情况下,有人可以将代码放入像The TypeScript Playground (link here!) 这样的独立IDE 中,然后立即着手解决问题,而无需首先重新创建它。所以不应该有拼写错误、不相关的错误或未声明的类型或值。 (可能应该删除该问题的任何“超出范围”的内容)
-
答案将是编译器仅在将布尔返回函数的返回值注释为类型谓词的特定情况下将
boolean推断为可分配给类型谓词。所以你必须添加一个像(x): x is AnimalFromType<T> => x.type === type这样的注解来编译它,因为那是boolean-returning函数,而不是createFilter()(一个boolean-returning-function-returning函数,这是不一样的事物)。我很乐意写一个答案,但如果可能的话,我想先把minimal reproducible example 和animals和AnimalFromType联系起来。 -
在解释为什么其他答案似乎没有解决这个问题时,我最终在this code here 中写了我希望是我自己的minimal reproducible example。如果该方法满足您的需求,您能否编辑问题以包含设置,我将发布答案,解释为什么它适用于我的版本但不适用于您的版本?
-
谢谢@jcalz。您的 MRE 有效,我将更新问题以等待您的回答。我不能说我理解 为什么 编译器无法理解从返回的 lambda 函数返回的布尔表达式表示外部函数的类型保护签名,但我想这只是一个限制TS?出于好奇,我可能会对 RC 进行 ping 操作。 :)
标签: typescript typeguards