在Typescript中可以使用Early-Return类型保护吗?

4xrmg8kj  于 2023-08-07  发布在  TypeScript
关注(0)|答案(1)|浏览(110)

我经常遇到这样的情况,在继续之前,我需要对函数中的变量进行类型检查。就我个人而言,我喜欢尽可能避免嵌套代码,并经常使用早期返回语句,以便函数的主要功能在最后并且不嵌套。对我来说,它使代码更具可读性和可维护性。但是,Typescript类型检查器似乎不喜欢这样。
下面的代码让类型检查器很生气,因为cupcake_name可能不是dessert变量的属性。

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)

kxkpmulp

kxkpmulp1#

TypeScript的控制流分析确实支持早期的return类型保护;问题不在于return,而在于typeGuardExpr === false检查没有被视为类型保护。
一般来说,你可以通过改变boolean类型的保护表达式是否有logical NOT prefix operator ( ! )来反转它的含义(注意,你不能只是在前面加上!;如果已经有一个!,那么添加一个将不起作用。!!typeGuardExpr不会被视为类型保护。如果!已经存在,则应该 * 删除 * 它。)对于您的示例,它看起来像:

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

字符串
但是typeGuardExpr === false不能以这种方式工作,原因很简单,因为没有人实现它。编译器不能分析代码的每一个可能的逻辑含义来缩小范围,因为这样的分析代价太高。相反,它使用启发式规则,检查特定的编程约定。而=== false并不是受支持的约定之一。
当然,这是可以改变的;他们可以实现一个检查,使typeGuardExpr === truetypeGuardExpr === false传播类型保护。但这在microsoft/TypeScript#9508中被建议并被拒绝。
添加这样的额外规则对编译器性能有可衡量的负面影响,这将不得不通过实际代码行为的切实改进来补偿。如果一个编程约定不是很常见,那么可能不值得增加编译时间来支持它。这似乎就是为什么microsoft/TypeScript#9508被拒绝的原因(请参阅此评论)。
尽管如此,这个问题在microsoft/TypeScript#31105上再次被提出,并被归类为bug。我在microsoft/TypeScript#53714上看到一个pull请求,可能正在等待合并。如果确实发生了这种情况,那么合并后的下一个TypeScript版本将突然支持您的原始代码!不过,目前还不清楚这是否会发生,也不清楚何时会发生。
因此,除非发生这种情况,否则我的建议是从typeGuardExpr === false切换到!typeGuardExpr(记住,确保不要以!!someOtherExpr结束)。
Playground链接到代码

相关问题