为什么nullish合并操作符不能在typescript中作为typeguard工作?

为什么nullish合并操作符不能在typescript中作为typeguard工作?,typescript,typeguards,nullish-coalescing,Typescript,Typeguards,Nullish Coalescing,在Typescript 3.7中引入了。这似乎是一个完美的类型保护的情况下,如 const fs=(s:string)=>s 常数fn=(n:number)=>n 设a:string | null |未定义 设b:number | null |未定义 常数x=(a??空)和&fs(a) 常数y=(b??空)和&fn(b) 但如果将该代码放入,它会在传递给fs/fn函数的a和b参数上向您发出警告,如: 我做了进一步的实验,发现这不仅仅是一个与null合并操作符无关的问题,而且当typescri

在Typescript 3.7中引入了。这似乎是一个完美的类型保护的情况下,如

const fs=(s:string)=>s
常数fn=(n:number)=>n
设a:string | null |未定义
设b:number | null |未定义
常数x=(a??空)和&fs(a)
常数y=(b??空)和&fn(b)
但如果将该代码放入,它会在传递给fs/fn函数的a和b参数上向您发出警告,如:

我做了进一步的实验,发现这不仅仅是一个与null合并操作符无关的问题,而且当typescript能够使用soemthing作为typeguard时,以及当不能使用soemthing作为typeguard时,我都无法改变主意(见下图)

最后两行最让我困惑。在我看来,分配给x7和x8的两个表达式是完全等效的,但在分配给x8的表达式中,typeguard可以工作,但对于x7表达式中的typescript来说,它似乎并不合适:

const fs=(str:string)=>str
常量create=(s:string)=>s=='s'?'字符串“:s=='n'?空:未定义
常量a:string | null | undefined=create('s')
常量b:string | null | undefined='s'
让x
如果(a!==null&&a!==未定义){
x=a
}否则{
x=fs(a)
}
常数x1=a!==null&&a!==未定义(&F)(a)
常数x2=a!==null&&a!==无效0&&fs(a)
常数x3=(a??空)和&fs(a)
常量x4=(b??空)和&fs(b)
常数x5=a!==null&&a!==未定义?答:财政司司长(甲)
const something=a!==null&&a!==未定义
常数x6=什么?答:财政司司长(甲)
常数x7=某物和fs(a)
常量x8=(a!==null&&a!==未定义)&&fs(a)
我不确定typescript是否因为某种原因无法应用类型保护,或者它是否确实是typescript中的一个bug。那么,typescript何时可以应用typeguard,何时不可以应用typeguard,是否有一种规则手册?或者它可能是一只虫子?或者我没有编译这些示例还有其他原因吗


顺便说一句,当使用一个用户定义的类型保护时,它当然可以完美地工作,但是,如果不必为typeguard添加一些运行时代码就可以了。

我花了很长时间试图写出一个机械的解释,解释为什么像
expr1 | | expr2&&expr3这样的特定表达式在某些情况下充当类型保护,而在其他情况下不充当类型保护。它最终变成了几页,但仍然不能解释示例中的所有情况。如果您关心,可以查看中为实现的代码


对这种限制和类似限制存在的原因有一个更高层次的解释:当你,一个人,看到一个联合类型的值时,你可以通过想象如果该值缩小到该类型的每个成员,在该值存在的整个范围内,会发生什么来决定分析它。如果您的代码在每个这样的案例分析中表现良好,那么它在整个联合中表现良好。这个决定可能是基于您对所讨论的代码行为的关注程度,或者我们不希望编译器重现的其他认知过程

编译器可能会一直对遇到的每一个可能的联合类型表达式进行这种分析。我们可以称之为“自动分布式控制流分析”,它的好处是几乎总是产生您想要的类型保护行为。缺点是编译器需要的内存和时间比您愿意花费的要多,而且可能比人类能够花费的还要多,这是由于每个额外的联合类型表达式对所需资源具有乘法效应时发生的组合爆炸。指数时间算法不适合编写好的编译器

有时,我希望能够向编译器提示,特定作用域中的特定联合类型值应该以这种方式进行分析,我甚至提出了这样一个“选择性加入分布式控制流分析”的请求(请参阅),但即使这样,也需要大量的开发工作来实现,并且会偏离TS的设计目标,即在不需要开发人员对控制流分析进行过多思考的情况下启用JS设计模式

因此,TypeScript语言设计者最终要做的是实现启发式,在有限的范围内执行此类分析,从而支持传统和惯用的JavaScript编码模式。如果像
(a??null)和&fs(a)
这样的代码对于语言设计者来说不够惯用和常规(这部分是主观的,部分取决于检查真实世界的代码集),并且如果实现它将导致编译器性能的严重损失,那我就不指望语言能支持它了

一些例子:

  • :支持将类型保护的结果“保存”到常量中(如
    something
    示例),以供以后使用。这是一个公开的建议,标记为“重新访问”,语言架构师不祥地宣布,很难以性能的方式完成它。这可能是惯用的,但可能很难有效地实现

  • :支持连续类型保护,其中一次对多个相关变量执行变窄。它太复杂了,因为要避免指数时间算法,您需要将它硬编码为一些少量的检查,这在一般情况下不会非常有益。语言维护者的建议是:使用更多的惯用检查



这是我能得到的最接近权威或官方答案。希望有帮助;祝你好运

我认为在您的示例中使用
没有任何意义<代码>a??空的
在任何情况下都只是
a
,除了
a===未定义的
,在这种情况下它是
空的
。如果你期望
(a??空)和&fs(a)
意味着“
空的
如果
a
是空的,或者
fs(a)
否则”,那么,那就不是了