我有一个与field1的接口,该接口具有多种可能的数据类型:数字、对象或布尔型.

在处理数据之前,我有If-Condition来判断数据类型是否为Numbers.

对于Case1和Case2,类型脚本正确识别字段1现在是一个数字类型,但不是在Case3上,这是我试图在我的项目中解决的问题.

How to Get TypeScrip知道,在 case 3中,field1现在是基于isGentNumber结果的数字类型?

我需要在不同的地方使用isGentNumber

interface Module1 {
  field1: number | {key: string; value: string}[] | boolean;
}

const value: Module1 = {
  field1: [{key: 'key', value: 'value'}]
};

if (typeof value.field1 === 'number' && value.field1 % 2 === 0 && (value.field1 > 0 || value.field1 < 100)) {
  const myString: number = value.field1; // Case1: WORKS
  console.log(myString);
}

const isCorrectNumberFn = (value: Module1): value is {field1: number} => (typeof value.field1 === 'number' && value.field1 % 2 === 0&& (value.field1 > 0 || value.field1 < 100));
if (isCorrectNumberFn(value)) {
  const myString: number = value.field1; // Case2: WORKS
  console.log(myString);
}

const isCorrectNumber = typeof value.field1 === 'number' && value.field1 % 2 === 0&& (value.field1 > 0 || value.field1 < 100);
if (isCorrectNumber) {
  const myString: number = value.field1; // Case3: ERROR: Type 'number | boolean | { key: string; value: string; }[]' is not assignable to type 'number'. Type 'boolean' is not assignable to type 'number'
  console.log(myString);
}

TypeScript playground

推荐答案

TypeScrip基于类型保护的narrow种表观类型值的能力仅限于明确实现的非常特定的场景.TypeScrip不是一个能够计算程序所有可能的future 状态的任意证明求解器.就编译器性能而言,这将是非常昂贵的.总会有这样的情况,在这种情况下,一个聪明的人可以说"我知道x在程序的这个点上一定是Y类型的",但TypeScrip不能识别这一点.有关示例,请参见microsoft/TypeScript#52822.

同样,类型脚本可以缩小类型的情况仅限于有人显式编程编译器这样做的情况.在这些情况下,让编译器判断是否可能进行这样的缩小所带来的额外复杂性和性能影响是由开发人员体验的整体改善来弥补的.

因此,例如,支持Direct typeof type guards:

if (typeof value.field1 === 'number' && ⋯) {
  const n: number = value.field1; // okay
}

此外,为了处理编译器不能自动按需键入Guard的情况,该语言允许用户编写自己的custom type guard functions:

const isCorrectNumberFn = (value: Module1): value is {field1: number} => (
  typeof value.field1 === 'number' && ⋯);

if (isCorrectNumberFn(value)) {
  const n: number = value.field1; // okay
}

但当然,并不是所有场景都受支持.你遇到了一个不受支持的情况:

const isCorrectNumber =
  typeof value.field1 === 'number' && ⋯;
if (isCorrectNumber) {
  const n: number = value.field1; // error!    
}

在TypeScrip4.4之前,这是不受支持的,因为根本不可能将类型保护的结果"保存"到另一个变量中并使用它来缩小范围.而是TypeScript 4.4 introduced support for narrowing based on aliased conditions saved into boolean variables,就像在microsoft/TypeScript#44730中实施的那样.

那么为什么上面的代码不起作用呢?这是因为"通过间接引用进行缩小只有在[...]被缩小的引用是一个const变量、一个readonly属性或一个在函数体中没有赋值的参数."您正在try 缩小value.field1,这是Module1的可变属性,而不是readonly property. 编译器不会为以后的控制流分析而试图保留isCorrectNumber的结果,因为您判断的值value.field1可能会在过渡期间重新分配. 编译器不想花额外的时间来判断,以确保这没有发生.它放弃了.


这直接导致了一种可能的替代方法:Make the 100 property 101,以便编译器知道使用它作为类型保护是相对安全的,即使不判断重新分配:

interface Module1 {
  readonly field1: (
    number | { key: string; value: string }[] | boolean
  );
}

然后这会突然起作用:

const isCorrectNumber =
  typeof value.field1 === 'number' && ⋯;
if (isCorrectNumber) {
  const n: number = value.field1; // okay
}

这样的方法可能并不总是合适的,但这是编译器目前所能做的最好的事情.如果你不能把field1当做readonly,那么也许从一开始就不要试图保留其类型的支票.您可以重构以使用直接判断,或者使用您负责维护正确性的自定义类型保护函数.

Playground link to code

Typescript相关问答推荐

如何从TypScript中的接口中正确获取特定键类型的所有属性?

在React中实现具有独立页面的嵌套路径的最佳实践是什么?

node—redis:如何在redis timeSeries上查询argmin?

如果一个变量不是never类型,我如何创建编译器错误?

我用相同的Redux—Toolkit查询同时呈现两个不同的组件,但使用不同的参数

将值添加到具有不同类型的对象的元素

从typescript中的对象数组中获取对象的值作为类型'

Vue SFC中的多个脚本块共享导入的符号

如何在Vue中使用Enum作为传递属性的键类型?

在保留类型的同时进行深层键名转换

下载量怎么会超过下载量?

使用泛型识别对象值类型

如何创建由空格连接的多个字符串的类型

类型脚本-处理值或返回值的函数

如何通过属性名在两个泛型数组中找到匹配的对象?

为什么TS2339;TS2339;TS2339:类型A上不存在属性a?

如何键入对象转换器

typescript如何从映射类型推断

如何在故事书的Typescript 中编写decorator ?

如何编写一个接受 (string|number|null|undefined) 但只返回 string 且可能返回 null|undefined 的函数?