#typescript #typescript-typings #typescript-generics
#typescript #typescript-типизации #typescript-дженерики
Вопрос:
У меня есть объект типа
type CompoundType = (IType1 | IType2)[] | (IType3 | IType4)[];
Где IType1, IType2, IType3, IType4, ... , ITypeN extend (A | B)
Я хочу написать обобщенную функцию для выполнения операции над каждым элементом CompoundType
объекта и привести возвращаемый тип к строгому SomeOperation<IType1 | IType2 | IType3 | IType4>[]
Что у меня уже есть:
const wrapWithOperation = (items: (A | B)[]): SomeOperation<A | B>[] => {
// implementation
}
К сожалению, возвращаемый тип wrapWithOperation(obj as CompoundType)
не SomeOperation<IType1 | IType2 | IType3 | IType4>[]
является, это SomeOperation<IType1 | IType2 | IType3 | ... | ITypeN>[]
. Я почти уверен, что это может быть достигнуто с помощью обобщений в определении типа указателя функции, но как мне это сделать?
Ответ №1:
Я не уверен, что полностью понял вашу настройку, но я думаю, что понял проблему. Это фиктивные типы, с которыми я работаю.
type IType1 = {1: string}
type IType2 = {2: string}
type IType3 = {3: string}
type IType4 = {4: string}
type CompoundType = (IType1 | IType2)[] | (IType3 | IType4)[];
type SomeOperation<T> = {
didSomething: T;
}
У нас есть функция, которая работает с массивом. Вместо того, чтобы передавать ему массив, в котором мы заранее знаем тип элемента, у нас есть массив, который может быть одного из двух типов. Сами эти типы: IType1 | IType2
и IType3 | IType4
являются объединениями других типов, но это не точка разбивки.
const wrapWithOperation = <T>(items: T[]): SomeOperation<T>[] => {
return items.map( i => ({didSomething: i}));
}
declare const x: CompoundType;
const res = wrapWithOperation(x); // error here
T
Кажется, что это сработает, но на самом деле это выдает ошибку, используя generic для представления каждого элемента массива. Typescript говорит, что CompoundType
это невозможно присвоить CompoundType
, что является реальным «а?», Если вы не понимаете шаг за шагом, что делает typescript.
Если вы хотите присвоить T
значение A | B
, typescript увидит, соответствует ли оно extends A
, и увидит, является ли оно extends B
и если оно равно true, тогда все в порядке. Но A | B
не расширяется A
или не расширяется B
таким образом по этой странной логике, A | B
не расширяется A | B
.
Итак, как нам это исправить?
Вместо этого мы назначаем общий T
для всего массива. Когда мы возвращаемся SomeOperation
, нам нужно знать тип каждого элемента массива T
, поэтому мы работаем infer
в обратном направлении.
const wrapWithOperation = <T extends any[]>(items: T): SomeOperation<T extends (infer X)[] ? X : never>[] => {
return items.map( i => ({didSomething: i}));
}
Теперь мы получаем результат с желаемым типом SomeOperation<IType1 | IType2 | IType3 | IType4>[]