Как проверить экземпляр generi c в массиве с Typescript - PullRequest
0 голосов
/ 03 марта 2020

У меня есть функция, которая получает массив, но они могут быть другого типа. В зависимости от типа мне нужно по-разному форматировать:

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

Я знаю, что могу использовать instanceof, чтобы проверить, есть ли у меня объект определенного класса в Typescript.

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.

Есть ли способ явно проверить наличие тип значений массива?

Ответы [ 2 ]

2 голосов
/ 03 марта 2020

Во время выполнения нет разницы между Array<Foo> и Array<Bar>; система типа stati c стерта из испущенного JavaScript, поэтому у вас есть только массив JavaScript. Поэтому вам нужно написать свой собственный тест, который работает во время выполнения, а затем сообщить компилятору о том, что вы делаете, чтобы получить преимущества от stati c typing.


Одним из способов было бы напишите несколько подходящих пользовательских функций защиты типов , которые позволят вам сделать это:

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
        }
    }
}

Компилятор понимает, что внутри истинного блока value было сужено от Foo[] | Bar[] до Foo[], а внутри ложного блока 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 для одного элемента массива и возвращает защиту нового типа, которая вызывает elemGuard для каждого элемента массива. Если все элементы проходят тест, то у вас есть массив охраняемого типа. Если хотя бы один элемент не проходит, значит, нет. При желании вы можете проверить только один элемент, но вы рискуете случайно обработать гетерогенный массив, такой как Array<Foo | Bar>, как Foo[]. Также обратите внимание, что это означает, что пустой массив [] всегда будет проходить тест; поэтому пустой массив будет считаться Foo[] и a 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)) - это защита составного типа, которая специально проверяет, является ли проверяемый массив Foo[], проверяя каждый элемент и выполняя проверку instanceof.


Хорошо, надеюсь, это поможет; удачи!

Детская площадка ссылка на код

1 голос
/ 03 марта 2020

Вам необходимо получить предмет и проверить этот тип. Но поскольку TypeScript имеет типизацию c, объекты массива всегда относятся к типу Foo.

...