Typescript 中是否可以提前返回 Typeguard?

Are Early-Return Typeguards Possible in Typescript?

提问人:Grant Thorshov 提问时间:8/7/2023 最后编辑:Grant Thorshov 更新时间:8/7/2023 访问量:135

问:

我经常遇到这样的情况,我需要在继续之前在函数中输入检查变量。就我个人而言,我喜欢尽可能避免嵌套代码,并经常使用 early-return 语句,以便函数的主要功能位于末尾且未嵌套。对我来说,它使代码更具可读性和可维护性。但是,Typescript 类型检查器似乎不喜欢这样。

下面的代码使类型检查器生气,因为它可能不是变量的属性。cupcake_namedessert

function what_kind_of_cupcake(dessert: Cupcake | Cookie){

    if (dessert instanceof Cupcake === false) return 

    console.log(dessert.cupcake_name)

}

但是,此代码满足 Typescript。

function what_kind_of_cupcake(dessert: Cupcake | Cookie){

    if (dessert instanceof Cupcake) {
        console.log(dessert.cupcake_name)
    }

}

我知道这两个函数在技术上都有效,并且两种编码风格都有时间和地点。这也是我面临的问题的一个非常简化的版本。我的问题是关于类型检查器如何理解这两个函数,以及是否有办法修改 TSconfig 以允许以前的编码风格。

为了解决这个问题,我有时会重新声明变量并使用关键字绕过类型检查器,尽管我宁愿不必继续这样做。as

function what_kind_of_cupcake(dessert: Cupcake | Cookie){

    // type of 'desert' is undetermined at this point. Could be a Cupcake or a Cookie
    if (dessert instanceof Cupcake === false) return

    // recast dessert? 
    dessert = dessert as Cupcake

    console.log(dessert.cupcake_name)
TypeScript 类型 铸造 TypeGuard 提前返回

评论

0赞 jcalz 8/7/2023
欢迎来到 Stack Overflow!如图所示,是否适合您而不是?如果是这样,我会写一个答案来解释;如果没有,我错过了什么?!(dessert instanceof Cupcake)dessert instanceof Cupcake === false
0赞 Grant Thorshov 8/7/2023
@jcalz哦,非常有趣,它绝对如此。你知道为什么这两种表达方式不同吗?
0赞 jcalz 8/7/2023
因为没有人在编译器中为类似的东西编写显式检查;这些事情需要直接实施;编译器无法计算代码流的所有可能的逻辑含义,并且仍然具有性能,因此这些是启发式方法。请参见 github.com/microsoft/TypeScript/issues/9508(otherTypeGuard) === false

答:

3赞 jcalz 8/7/2023 #1

TypeScript 的控制流分析确实支持早期类型的防护;问题不在于 ,而在于检查不被视为类型保护。returnreturntypeGuardExpr === false

一般来说,你总是可以通过改变表达式是否具有逻辑 NOT 前缀运算符 (!) 来反转类型保护表达式的意义(请注意,你不能只在前面加上一个;如果已经有一个 ,那么添加一个是行不通的。 不会被视为类型后卫。如果它已经存在,则应将其删除。对于您的示例,如下所示:boolean!!!!typeGuardExpr!

function whatKindOfDessert(dessert: Cupcake | Cookie) {
  if (!(dessert instanceof Cupcake)) return;
  console.log(dessert.cupcakeName) // okay
}

但不能以这种方式工作,仅仅是因为没有人实现它。编译器无法分析代码中所有可能的逻辑含义以缩小范围,因为这种分析的成本高得令人望而却步。相反,它使用启发式规则,检查特定的编程约定。只是不是受支持的约定之一。typeGuardExpr === false=== false

当然,这是可以改变的;他们可以实现一个检查,使类型保护并传播。但这是在 microsoft/TypeScript#9508 中建议的,但被拒绝了。typeGuardExpr === truetypeGuardExpr === false

添加这样的额外规则会对编译器性能产生可衡量的负面影响,而这必须通过对实际代码行为的切实改进来支付。如果编程约定不是很常见,则可能认为不值得增加编译时间来支持。这似乎就是 microsoft/TypeScript#9508 被拒绝的原因(请参阅此评论)。

尽管如此,该问题还是在 microsoft/TypeScript#31105 上再次提出,并被归类为错误。我在 microsoft/TypeScript#53714 上看到一个拉取请求,等待可能合并。如果确实发生了这种情况,那么合并后的下一个 TypeScript 版本将突然支持您的原始代码!不过,目前尚不清楚这是否会发生,也不清楚何时发生。

因此,除非发生这种情况,否则我的建议是从切换到(确保不要以 ,记住)。typeGuardExpr === false!typeGuardExpr!!someOtherExpr

Playground 代码链接