Типскрипт объединяет массив различных универсальных типов в один и тот же универсальный тип

#typescript #typescript-generics

Вопрос:

У меня есть массив следующих типов:

 myList = [
 listOfString: Parser<string[], string, any>
 stringThing: Parser<string, string, any>
]
 

и typesript помечает список типов (Parser<string[], string, any> | Parser<string, string, any>)[]

Есть ли способ преобразовать это в Parser<string | string[], string, any> по умолчанию?

Альтернативно, при передаче этого в функцию, которая выглядит следующим образом

 myFunc<T>(list: Parser<T>[]): Parser<T>
 

есть ли способ разрешить этот тип массива и сделать его myFunc возвращающим Parser<string | string[], string, any> ?

Комментарии:

1. Пожалуйста, введите свой код на игровой площадке

2. Почему бы вам просто явно не объявить тип myList ? myList:Parser<string[]|string, string, any>[]

3. Вообще говоря Array<A>|Array<B> , это не тот же тип, что и, Array<A|B> поэтому преобразование одного в другое было бы неправильным.

Ответ №1:

Есть ли способ преобразовать это в Parser<string | string[], string, any> […]?

T<string> | T<string[]> и T<string | string[]> не являются эквивалентными типами в общем случае.

Рассмотрим в качестве примера

 type T<U> = (u: U) => U;
 

Теперь T<string> | T<string[]> либо функция принимает строку, либо функция принимает массив строк. В любом случае параметр функции является только одним из этих двух типов, и вы можете вызвать его только с аргументом этого типа. Если вы (можете) вызвать его со строкой, вы не можете вызвать его с массивом строк, и он также должен возвращать строку.

С другой стороны, T<string | string[]> описывает функцию, параметром которой является строка или массив строк, что означает, что для каждого отдельного вызова вы можете выбрать, какой из них вы передаете. Также возможно, что вы вызовете его со строкой, но функция вернет массив строк.

С конкретным примером, (u: string) => u удовлетворял бы первому типу, но не может быть присвоен второму:

 // OK
const test1: T<string> | T<string[]> = (u: string) => u;

// Error
const test2: T<string | string[]> = (u: string) => u;
 

Аналогично, (u: string | string[]) => u имеет противоположный эффект:

 // Error
const test3: T<string> | T<string[]> = (u: string | string[]) => u;

// OK
const test4: T<string | string[]> = (u: string | string[]) => u;
 

Комментарии:

1. Для примера без контравариантности, type Thing<T> = {a: T, b: T} позволил {a: string, b: string[]} бы иметь тип Thing<string | string[]> , но не тип Thing<string> | Thing<string[]> .