为什么Typescript在被类型守卫消除时不会缩小通用联合类型组件?

时间:2018-01-18 09:53:45

标签: typescript

给出以下类型定义和函数实现:

interface WithNumber {
  foo: number;
}

interface WithString {
  bar: string;
}

type MyType = WithNumber | WithString;

interface Parameter<C extends MyType = MyType> {
  container: C
} 

function isParameterWithNumberContainer(arg: Parameter): arg is Parameter<WithNumber> {
  return typeof (arg.container as WithNumber).foo === "number";
}

function test(arg: Parameter<WithNumber | WithString>) {
  if (isParameterWithNumberContainer(arg)) {
    arg.container.foo;
    return;
  }
  /*
   * Error:
   *   Property 'bar' does not exist on type 'MyType'.
   *     Property 'bar' does not exist on type 'WithNumber'.
   */
  arg.container.bar;
}

为什么Typescript没有缩小if-block下方arg的类型,以防御类型后卫?在我看来,arg不可能是Parameter<WithString>之外的东西。然而,Typescript仍然认为它是Parameter<WithNumber | WithString>,因此在尝试访问arg.container.bar时会出错。

TS Playground link

1 个答案:

答案 0 :(得分:3)

我试图找到适当的文件,但我不能。我对类型保护与if语句一起使用的方式的理解是,then分支上的类型被排除在else分支上的联合类型之外。它没有深入探讨可以排除的类型。您的参数是一个泛型类型,其联合类型作为通用参数,因此else分支上不会排除任何内容。

一个简单的解决方法是使参数成为联合类型:

function isParameterWithNumberContainer(arg: Parameter): arg is Parameter<WithNumber> {
    return typeof (arg.container as WithNumber).foo === "number";
}

function test(arg: Parameter<WithNumber> | Parameter<WithString>) {
    if (isParameterWithNumberContainer(arg)) {
        arg.container.foo;
        return;
    }
    arg.container.bar;
}