如何使用 Typescript 检查数组中泛型的实例

How to check instance of the generic in Array with Typescript

我有一个接收数组的函数,但它们可以是不同类型的。根据我需要不同格式的类型:

public format(value: Foo[] | Bar[]) {
  // this does not work
  if (value instanceof Foo[]) ...
}

我知道我可以使用 instanceof 检查 Typescript 中是否有某个 class 的对象。

new Foo() instanceof Foo // true

这也适用于检查我是否有数组。

new Array<Foo> instanceof Array // true

但我无法检查我的数组是否真的输入了 Foo

new Array<Foo>() instanceof Array<Foo> 
// The right-hand side of an 'instanceof' expression must be of type 'any' 
// or of a type assignable to the 'Function' interface type.

有什么方法可以显式检查数组值的类型吗?

您需要检索一个项目并检查该项目的类型。但是因为 TypeScript 有静态类型,数组的对象总是来自 Foo 类型。

在运行时Array<Foo>Array<Bar>没有区别;静态类型系统是 erased 来自发出的 JavaScript,所以你所拥有的只是一些 JavaScript 数组。因此,您需要编写自己的在 运行 时间运行的测试,然后告诉编译器您在做什么,以便获得静态类型的好处。


一种方法是编写一些合适的 user-defined type guard functions,这样你就可以这样做:

public format(value: Foo[] | Bar[]) {
    const isFooArray = isArrayOf(isInstanceOf(Foo));

    if (isFooArray(value)) {
        // true block
        for (const foo of value) {
            const f: Foo = foo; // okay
        }
    } else {
        // false block
        for (const bar of value) {
            const b: Bar = bar; // okay
        }
    }
}

编译器理解在 true 块内,value 已从 Foo[] | Bar[] 缩小到 Foo[],而在 false 块内,value 已缩小从 Foo[] | Bar[] 缩小到 Bar[]。这与 isFooArray() 的类型签名有关,它是通过组合其他两个函数 isArrayOf()isInstanceOf().

的输出创建的类型保护

让我们检查一下它们的定义:

const isArrayOf = <T>(elemGuard: (x: any) => x is T) =>
    (arr: any[]): arr is Array<T> => arr.every(elemGuard);

函数 isArrayOf() 为单个数组元素采用类型保护函数 elemGuard 和 returns 对数组的每个元素调用 elemGuard 的新类型保护.如果所有元素都通过了测试,那么你就有了一个受保护类型的数组。如果连一个元素都没有通过,那么你就没有通过。如果你愿意,你可以只检查一个元素,但是你 运行 不小心将 Array<Foo | Bar> 之类的异构数组视为 Foo[] 的风险。另请注意,这意味着空数组 [] 将始终通过测试;所以一个空数组将被认为是 Foo[] Bar[].

const isInstanceOf = <T>(ctor: new (...args: any) => T) =>
    (x: any): x is T => x instanceof ctor;

isInstanceOf() 函数只是将正常的 instanceof 测试包装到适合与 isArrayOf() 一起使用的用户定义类型保护中。

所以 const isFooArray = isArrayOf(isInstanceOf(Foo)) 是一个复合类型保护,它通过检查每个元素并进行 instanceof 检查来专门检查它正在检查的数组是否是 Foo[]


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

Playground link to code