【问题标题】:Narrowing down a type without having to declare an ad-hoc type guard function?在不必声明临时类型保护函数的情况下缩小类型?
【发布时间】:2021-12-04 06:38:58
【问题描述】:

说,我有一个这样的类,TypeScript 对此并不满意:

class Foo extends ThirdPartyLibrary {
  animal!: Animal;

  bar(): void {
    this.animal.quack(); // Method `quack` does not exist on type `Animal` ????
  }
}

解决此问题的官方方法似乎是使用临时类型缩小功能:

class Foo extends ThirdPartyLibrary {
  animal!: Animal;

  bar(): void {
    function isDuck(maybeDuck: unknown): maybeDuck is Duck {
      return maybeDuck?.look === 'ducky' && maybeDuck?.voice === 'quacky';
    }

    if (!isDuck(this.animal)) {
      throw new Error('Expected argument to be a duck');
    }

    this.animal.quack();
  }
}

在某些情况下,这种运行时检查是多余的。例如,当使用类型不佳的第三方库时,我不想在运行时测试该库并编写所有样板代码。

相反,我想写得越短越好。

到目前为止,我最好的尝试是使用as 类型转换将变量重新分配给另一个变量:

class Foo extends ThirdPartyLibrary {
  animal!: Animal;

  bar(): void {
    const animal = this.animal as unknown as Duck;

    animal.quack(); // Now TypeScript knows that this animal can quack
  }
}

但我不需要那个变量!我只需要 TypeScript 静态知道类型,我不想声明任何运行时变量或 if 子句或抛出的错误。

我正在成像这样的东西:

class Foo extends ThirdPartyLibrary {
  animal!: Animal;

  bar(): void {
    this.animal is Duck; // Narrow down the type without boilerplate

    this.animal.quack();
  }
}

我只想让 TypeScript 静态地知道在代码中这个变量/属性肯定是特定类型的。我该怎么做?


对我不起作用的解决方案:

  1. animal 属性键入为Duck

    class Foo extends ThirdPartyLibrary {
      animal!: Duck;
    }
    

    这个解决方案对于我的简单示例来说是最合理的,但这只是我想出的一个最小的人工示例。

    想象Foo 类比这更复杂:this.anmial 被多次使用,大多数时候它可以是任何Animal。只有在代码中的这个特定点,它才能确定是鸭子。

  2. 使用内联类型转换:(this.animal as unknown as Duck).quack()

    这行得通,但是当你需要对这种动物做不止一件鸭子的事情时,这种方法会变得很烦人:

    (this.animal as unknown as Duck).quack();
    (this.animal as unknown as Duck).waddle();
    (this.animal as unknown as Duck).awwYiss("Motha. Funkin. Bread crumbs!");
    
  3. 修复第三方库的类型。假设这些类型真的很复杂,而您没有能力深入研究它们。

【问题讨论】:

    标签: typescript typeguards type-narrowing


    【解决方案1】:

    您可以定义一个没有那么多运行时成本的幻象缩小谓词函数:

    function narrow<U>(x: any) : x is U { return true }
    
    ...
    
    bar(): void {
      if (narrow<Duck>(this.animal))
        this.animal.quack();
    }
    

    但我认为这与您的最佳期望还不够接近。

    【讨论】:

    • 谢谢!我相信你的例子可以简化为if (this.animal insanceof Duck) {}。但我正在尝试为无法一概而论的情况找到解决方案,例如。 G。第三方 API 可以提供此特殊属性:if (this.anmial?.__isDuck__)。 TypeScript 不会将其识别为更窄的类型保护功能,并且通用类型保护功能无法涵盖所有​​可能的情况。
    猜你喜欢
    • 1970-01-01
    • 2019-03-02
    • 1970-01-01
    • 2022-10-13
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多