为什么在使用Array.isArray时会丢失类型推断?

时间:2019-11-15 11:49:51

标签: typescript

我只是想知道为什么智能感知的类型推断在Array.isArray条件内丢失了。

请考虑以下代码段:

type T = {
    readonly name: string;
    readonly descr: string;
}

interface I{
    readonly tags: ReadonlyArray<T>;
}

function Z(arg: I): void{
    const { tags } = arg;
    if (Array.isArray(tags)) {  //hovering "tags" here shows "readonly T[]"
        for (let t of tags) {   //hovering "tags" here shows "any[]"

        }
    }
}

Z({
    tags:[]
})

换句话说,为什么原始类型不从其声明中保留,而是更改为获取isArray签名?

在Visual Studio以及playground中进行了测试。

2 个答案:

答案 0 :(得分:1)

这是已知的issue,带有ReadonlyArray和类型保护功能Array.isArray。您还可以找到可能的解决方法here

  

换句话说,为什么原始类型不从其声明中保留,而是更改为获取isArray签名?

部分原因是Array<any>实际上是ReadonlyArray<any>子类型

type IsArraySubtypeOfROArray = Array<any> extends ReadonlyArray<any> ? true : false // true
type IsROArraySubtypeOfArray = ReadonlyArray<any> extends Array<any> ? true : false // false

正如Maciej在他的回答中所说,没有必要检查,因为您已经可以确定在上述情况下有一个数组。因此,假设属性tags的类型为T | ReadonlyArray<T>,使其变得更有趣。

带有内置的ArrayConstructor界面

interface ArrayConstructor {
  ...
  isArray(arg: any): arg is any[];
}

并给定Array.isArray(tags)返回true,则编译器将T | ReadonlyArray<T>的类型tagsany[]的返回类型isArray进行比较。 TReadonlyArray<T>都不是any[]的子类型。因此,如果any[]TReadonlyArray<T>的子类型,则编译器会采用另一种方式。与ReadonlyArray<T>的情况一样,控制流分析现在解析为any[]作为可能的最窄类型。

这是一个playground,带有正确键入的示例。

答案 1 :(得分:0)

问题出在

Array.isArray(tags)

如果通过此类型保护,则说明您正在使用any[]。如果您检查某物是否为数组,这对我们来说是可以理解的。该防护不检查元素,并且由于数组可以为空,因此很难检查。使用Array.isArray的标准情况是您不知道所得到的结果(例如,联合类型(其中value可以是数组,也可以不是数组)),并且您想确保它是一个数组。接下来就是检查数组。

您的示例非常不常见,因为您无需进行检查,因为您已静态设置为获取T[]作为输入。因此,对于类型系统Array.isArray(tags)毫无意义,因为tags是T的数组。

您可以通过自定义类型防护来解决此问题:

const isArray = <V>(a: any): a is V[] => Array.isArray(a);
// above guard set additional type information to the array element

// usage
if (isArray<T>(tags)) {  //hovering "tags" here shows "readonly T[]"
        tags // here tags are T[]
    }

但是在您的示例中,直到输入的具体程度不如T[]才有意义,例如,对于T | T[]这样的输入就有意义:

interface I{
    readonly tags: ReadonlyArray<T> | T; // union type
}

然后检查就有意义了,因为我们在这里分支以对T[]T进行不同的处理。