Добиться более строгого ввода возврата в функции с помощью сложного ввода параметров

#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>[]