TypeScript-防止null的类型防护

时间:2019-08-24 16:28:02

标签: typescript

我在null对象道具中使用了下面的防护类型,但仍然出现错误:

function a(par: {a: string; b: null | string}): {a: string; b: string} | undefined {
  if (par.b === null) {
    return;
  }

  return par;
}
  

输入'{a:string; b:字符串|空值; }'不能分配给类型'{   a:字符串; b:字符串; }'。属性“ b”的类型不兼容。       输入'string | “ null”不可分配给“ string”类型。         不能将'null'类型分配给'string'类型。

我认为,如果我检查par.b === null,TS应该推断不可能返回具有prop.b === null的对象。

还是在这里让我感到困惑?

2 个答案:

答案 0 :(得分:4)

TL; DR:您并不疯狂,但是您期望编译器提供的功能超出了预期。使用类型断言并继续。


TypeScript不能确定类型保护检查的所有可能含义。当检查对象的属性为discriminated union且要检查的属性为判别属性时,检查对象的属性会缩小对象本身类型的少数地方之一。在您的情况下,par本身甚至不是联合体类型,更不用说是受歧视的联合体了。因此,当您检查par.b时,编译器会缩小par.b的范围,但不会将该缩小范围向上传播为par的范围。

可以做到这一点,但是问题在于,这种计算对于编译器而言很容易变得昂贵,如this comment by one of the language architects中所述:

  

对于控制流程图中的每个对x的引用,我们现在必须检查每个以点名命名为x作为基本名称的类型防护。换句话说,为了知道x的类型,我们必须查看x属性的所有类型防护。那有可能产生很多工作。

如果编译器像人一样聪明,则只有在可能有用时,才可能执行这些额外的检查。或者,一个聪明的人可以写出一些启发式的方法,对这个用例来说已经足够了。但是我认为实际上,进入该语言的优先级并不高。我没有找到建议这样做的open issue in GitHub,因此,如果您对此有强烈的兴趣,则可能要提出一个。但是我不知道它会收到多好。

在没有更聪明的编译器的情况下,有一些解决方法:


最简单的解决方法是接受您比编译器更聪明的信息,并使用type assertion来告诉您您确定自己在做的事是安全的,并且不必为验证它担心太多。通常,类型断言有些危险,因为如果您使用一个类型断言,并且断言是错误的,那么您只会对编译器撒谎,而由此引起的任何运行时问题都是您的错。但是在这种情况下,我们可以很自信:

function aAssert(par: {
  a: string;
  b: null | string;
}): { a: string; b: string } | undefined {

  if (par.b === null) {
    return;
  }

  return par as { a: string; b: string }; // I'm smarter than the compiler ?
}

这可能是解决问题的方法,因为它可以使您的代码保持基本相同,并且断言相当温和。


另一种可能的解决方法是使用user-defined type guard函数来缩小par的范围。这有点棘手,因为对联合类型不起作用的类型保护函数不会在“ else”分支中缩小范围……可能是因为该语言缺少negated types。也就是说,如果您有类型防护function guard(x: A): x is A & B,并且调用if (guard(x)) { /*then branch*/ } else { /*else branch*/ },则x在“ then”分支内将缩小为A & B,而只是{{ 1}}在“ A”分支中。没有else类型可以使用。您能获得的最接近的结果是进行A & not B,但这只是切换哪个分支变窄。

所以我们可以这样做:

if (!guard(x)) {} else {}

function propNotNull<T, K extends keyof T>( t: T, k: K ): t is { [P in keyof T]: P extends K ? NonNullable<T[P]> : T[P] } { return t[k] != null; } 后卫将在返回propNotNull(obj, key)的情况下,将true缩小为已知obj不是obj.key(或{ {1}} ...仅因为NonNullable<T>是标准实用程序类型)。

现在您的null函数可以写为:

undefined

支票a()导致function aUserDefinedTypeGuard(par: { a: string; b: null | string; }): { a: string; b: string } | undefined { if (!propNotNull(par, "b")) { return; } else { return par; } } 在该第一个分支中根本不缩小,而在第二个分支中将!propNotNull(par, "b")缩小为par。这足以使您的代码编译器没有错误。

但是我不知道与类型断言相比是否值得额外的复杂性。


好的,希望能有所帮助;祝你好运!

Link to code

答案 1 :(得分:0)

如果我不介意,如果对象par为null,则par.b是错误