繁体   English   中英

打字稿可能的代码路径与打字漏洞?

[英]Typescript possible code paths vs. typing loophole?

给定代码

class X {a:string = "a"}
class Y {b:number = 1}

// does not compile
function what(v: X|Y) {
  if (v instanceof X) {
    return "its an X";
  }
  if (v instanceof Y) {
    return "its a Y";
  }
  //missing return
}

打字稿编译器说:

error TS7030: Not all code paths return a value.

没错,因为结构化类型,我可以将what称为

let v = {b:1}
what(v)

从那时起v在结构上与Y兼容。 现在我改变what

function what(v: X|Y) {
  if ("a" in v) {
    return "its an X";
  }
  if ("b" in v) {
    return "its a Y";
  }
  // missing return
}

仍然会出现编译错误。

我想知道编译器是否无法得出将采用if分支之一的方法,还是存在漏洞仍然允许我传递与两个if分支都不匹配的兼容对象。

这是TypeScript中的设计限制 您可以合理地期望穷举检查将使编译器意识到最后if语句之后的代码不可访问。 但这并没有发生,而且看起来他们也不打算很快解决这个问题。

让我们看看我们可以自己做些什么。 在下面的代码中:

class X { a: string = "a" }
class Y { b: number = 1 }

// does not compile
function what(v: X | Y): string {
  if (v instanceof X) {
    return "its an X";
  }
  v // Y
  if (v instanceof Y) {
    return "its a Y";
  }
  v // never
}

实际上,编译器确实会在传递这些return语句时缩小v的类型……它抱怨,但这不是因为它认为当您离开函数底部时v可能仍然是XY 它抱怨是因为它与v的变窄无关地分析代码路径,因此它看到一个代码路径,该代码路径最后隐式返回undefined 解决此问题的唯一方法是确保所有代码路径中都有returnthrow

再次注意, v首先收窄至Y ,然后never 解决错误的一种方法是利用缩小到Y ,然后在那里进行return ,而不是第二个if测试:

function what(v: X | Y): string {
  if (v instanceof X) {
    return "its an X";
  }
  v // Y
  return "its a Y";
}

现在没有错误。 如果您信任instanceof X检查的instanceof X ,则可能是这样。 到达此处的另一种标准方法是使用穷举检查辅助函数assertNever()

function assertNever(x: never): never {
  throw new Error("OH NOES!!");
}

如果调用assertNever()函数, assertNever()引发错误,因此它never返回。 但是它要求其参数的类型必须是never ,因此调用它应该是编译器错误,除非代码不可访问:

function what(v: X | Y): string {
  if (v instanceof X) {
    return "its an X";
  }
  v // Y
  if (v instanceof Y) {
    return "its a Y";
  }
  v // never
  return assertNever(v);
}

现在what()已知what()返回string | never string | never代替string | undefined string | undefined string | never string | never不仅仅是string 而且您可以看到它如何作为穷举检查,因为如果您不将v缩小为never ,它将引发错误:

function oops(v: X | Y): string {
  if (v instanceof X) {
    return "its an X";
  }
  return assertNever(v); // error, v is Y, not never
}

好吧,希望有帮助。 祝好运!

我知道使用接口和标记联合的其他版本。 并且此版本可以正确编译,并且所有代码路径都返回一个值。

interface IX {kind: "a"; a: string }
interface IY {kind: "b"; b: number }

function what2(v: IX|IY): string {
    switch (v.kind) { 
        case "a":
         return "its an X";
         case "b":
         return "its an Y";
    }
}

var r1 = what2({kind: "a", a: "hello"});
var r2 = what2({kind: "b", b: 33});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM