【问题标题】:Type guard with other variable带有其他变量的类型保护
【发布时间】:2021-07-03 19:44:09
【问题描述】:

是否可以让其他变量充当类型保护?

假设一些这样的代码

let foo: string | null = Math.random() > .5 ? null : 'bar'
const otherProp = true
const test = foo !== null && otherProp
function foobar(x: string){}

如果我现在打电话给foobar(foo),我会假设一个警告,因为 foo 可以是 null 或字符串,并且只允许使用字符串

但如果我这样称呼它

if (test){
    foobar(foo)
}

它不应该警告我,因为 test 仅在 foo 不为 null 时才为真,因此只有 string 保持为类型

这样的事情可能吗?

Playground

【问题讨论】:

  • 如果您将foo !== null 检查直接放入 if 语句中,Typescript 会理解它。你不这样做有什么原因吗?
  • 是的。有助于可读性。在我的生产代码中,if 部分在几个地方我不想在所有地方都内联它
  • 您可以使用自定义类型保护isNotNull = <T>(x:T): x is Exclude(T, null) => x !== null,然后可以轻松地重复使用if (isNotNull(foo))
  • @VLAZ 这意味着我需要在每个 if 中调用一个函数,并且不能预先计算一次。所以这意味着更多的计算只是为了让 TS 开心似乎不是一个好的选择
  • @L.A 是的,拥有一个功能会成功,因此您每次都必须重新评估它。然而,让我们看看另一面——如果你计算一次并想使用缓存的结果,你现在有不可重用的代码。如果您需要在不同的范围内进行相同的检查,则必须重复检查。无论如何,您必须将其提取到(有效地)类型保护中才能重用它。假设你的类型守卫做了一个简单的操作,比如空检查或属性查找,它不会对性能产生影响。如果它真的很贵,你仍然可以记住守卫。

标签: typescript typescript-typings


【解决方案1】:

目前这在 TS 中是不可能的。因为它给他们的控制流分析器增加了相当大的复杂性。除了明确地将foo !== null 设置为if expression 之外,还有两种可能的做法:

1- 使用Assertions:

if (test){
    // since test is equal to (foo !== null)
    foobar(foo as string)
}

但这有点容易出错,因为您将来可能会更改 test 变量,然后该类型断言会导致错误,因为 foo 不再肯定是 string

2-User-Defined Type Guards

在我看来,更好的方法是使用@VLAZ 正确提到的用户定义类型保护。

   const isNotNull = <T>(x:T): x is Exclude(T, null) => x !== null
    
   if (isNotNull(foo)){
     foobar(foo)
   }

【讨论】:

    【解决方案2】:

    目前这是不可能的(请参阅this issue)。

    这里有一个解决方法。它为wrappedFoo 分配一个类型,该类型要么包含字符串且为真,要么为空且为假。检查布尔值后,Typescript 知道它是这两个中的哪一个:

    const foo: string | null = Math.random() > .5 ? null : 'bar'
    const wrappedFoo = foo === null ? { foo, isString: false as const } : { foo, isString: true as const }
    
    if (wrappedFoo.isString){
        console.log(wrappedFoo.foo.length)
    } else {
        console.log(wrappedFoo.foo.length) // Error Object is possibly 'null'
    }
    

    【讨论】:

      【解决方案3】:

      Typescript 的下一个版本 (4.4) will support control flow analysis of conditional expressions。您需要做的唯一更改是let foo -> const foo,因为

      仅当条件表达式或判别属性访问在没有类型注释的 const 变量声明中声明并且被缩小的引用是 const 变量、只读属性或函数体中没有赋值

      Playground v4.4

      【讨论】:

        猜你喜欢
        • 2013-07-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-09
        • 2021-10-04
        • 2019-03-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多