可以将 TypeScript 类型谓词分配给变量吗

Can a TypeScript type predicate be assigned to a variable

考虑来自 TypeScript docs 的类型谓词:

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

我有一些这样的代码:

function inRightPlace(pet: Fish | Bird) {
  return (
    (isFish(pet) && pet.inWater()) ||
    (!isFish(pet) && pet.inSky())
  );

我想通过将结果分配给一个变量然后检查它来避免调用 isFish 两次。像这样:

function inRightPlace(pet: Fish | Bird) {
  const isAFish = isFish(pet);
  return (
    (isAFish && pet.inWater()) ||
    (!isAFish && pet.inSky())
  );

TypeScript 编译器不喜欢这样,因为 isAFish 的检查不作为对 pet 类型的公认测试。我试过给 isAFish 一个 (pet is Fish)(typeof(pet) is Fish) 的类型,但看起来类型谓词不能像这样用作变量的类型。

我的印象是这里无法避免对类型保护函数的重复调用,但我在网上找不到太多相关信息,因此无论哪种方式都希望得到确认。

TS4.4 更新:

TypeScript 4.4 将支持将类型保护的结果保存到 const,如 microsoft/TypeScript#44730 中所实现的。此时,您的代码示例将正常运行:

function inRightPlace(pet: Fish | Bird) {
  const isAFish = isFish(pet);
  return (
    (isAFish && pet.inWater()) ||
    (!isAFish && pet.inSky()) 
  ); // okay
}

Playground link to code


TS4.3 及以下版本的答案:

不,您不能将用户定义的类型保护的结果分配给变量并以相同的方式使用它。之前有人建议过这样的功能:请参阅 microsoft/TypeScript#24865 and/or microsoft/TypeScript#12184。如果你关心这个,你可以去那里给它一个。

但是,对于您的特定示例,我建议使用 ternary operator(x && y) || (!x && z) 替换为 x ? y : z(如果 y || false 则可能 x ? (y || false) : zy 的差异足以让您关心)。这只会评估一次用户定义的类型保护,编译器对三元运算符的了解足以缩小控制流:

function inRightPlace(pet: Fish | Bird) {
    return isFish(pet) ? (pet.inWater() || false) : pet.inSky(); // okay
}

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

Link to code