首页 > 解决方案 > 如何将类型谓词分配给变量

问题描述

我有一个代码:

let point: number | undefined;
let point2: number;

const isPointNotUndefined = point !== undefined;
if (isPointNotUndefined) {
  point2 = point;
}

TypeScript 说“Type 'number | undefined'不可分配给类型number”但如果我接下来这样做:

if (point !== undefined) {
  point2 = point;
}

这很好用。

如何解释打字稿在变量中执行类型谓词,而不创建函数?

标签: typescript

解决方案


TS4.4 更新:

TypeScript 4.4 将引入对将某些类型保护的结果保存到 a 的支持const,如microsoft/TypeScript#44730中所实现的那样。不幸的是,您在此处的特定示例将继续不起作用,因为point它是一个可变变量,并且编译器不会花费必要的资源来确保在分配和检查它point之间实际上没有重新分配。isPointNotUndefined如果您创建point一个const或一个函数参数,那么编译器将假定它point没有被重新分配并将isPointNotUndefined用作类型保护:

function foo(point: number | undefined) {
  let point2: number;
  const isPointNotUndefined = point !== undefined;
  if (isPointNotUndefined) {
    point2 = point; // okay
  }
}

Playground 代码链接

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

目前在 TypeScript 中无法将类型保护的结果存储在布尔变量中。GitHub 中有两个开放的功能请求:microsoft/TypeScript#12184microsoft/TypeScript#24865。根据TypeScript 首席架构师的评论

这将要求我们跟踪一个变量的特定值对其他变量的影响,这将增加控制流分析器的大量复杂性(以及相关的性能损失)。不过,我们会将其保留为建议。

因此,它不太可能很快实现,除非有人可以提出一种不会显着降低编译器性能的方法。如果你有这样的提议,你可能想去#24865 并提出它。否则,您总是可以给他们每个人添加另一个投票,但务实地说,您应该继续进行,就好像这永远不会发生一样。


解决方法是您已经知道但不想使用的方法。为了完整起见,您可以执行内联检查:

if (point !== undefined) {
  point2 = point;
}

或者如果检查写起来很麻烦,请为其创建一个用户定义的类型保护函数

const isDefined = <T,>(x: T | undefined): x is T => typeof x !== "undefined";
if (isDefined(point)) {
  point2 = point;
}

由于检查undefined是如此之快,我怀疑任何其他解决方法都不会那么麻烦。不过,另一种方法是将任何“检查有效性和值”操作表示为有区别的 union,例如:

type DefinedUnion<T> =
  { defined: true, value: Exclude<T, undefined> } |
  { defined: false, value: undefined };

可区分联合具有将“检查”存储为可区分属性的优点。当然,将值转换为 a 的形式DefinedUnion需要更多代码:

function toDefinedUnion<T>(x: T | undefined): DefinedUnion<T> {
  return { defined: typeof x !== "undefined", value: x } as DefinedUnion<T>
}   

const pointU = toDefinedUnion(point);

但是它很容易使用。

if (pointU.defined) {
  point2 = pointU.value; // okay
}

同样,这不是我建议替换单个“is this undefined”检查的方法。但是,如果您的“这是一件有效的事情”检查足够昂贵,以至于您真的只想做一次,那么有区别的工会可能是要走的路。


Playground 代码链接


推荐阅读