Typescript: изменить тип функции, чтобы она возвращала новое значение

В принципе, я хочу что-то вроде этого:

export type ReturnValueMapper<Func extends (...args: Args[] /* impossible */ ) => any, ReturnValue> = (...args: Args[]) => ReturnValue;

Я почти уверен, что это невозможно, но я не нашел точного подтверждения.


Вариант использования улучшает типы для рекомпозиций withStateHandlers, позволяя определять средства обновления состояния, например:

interface StateUpdaters {
    update(field: string): void; // I don't want to specify Partial<State> here 
}

1 ответ

редактировать

Поскольку на первоначальный вопрос был дан ответ, машинопись улучшила возможное решение этой проблемы. С добавлением Tuples в параметры отдыха и выражения распространения нам теперь не нужно иметь все перегрузки:

type ArgumentTypes<T> = T extends (... args: infer U ) => infer R ? U: never;
type ReplaceReturnType<T, TNewReturn> = (...a: ArgumentTypes<T>) => TNewReturn;

Это не только короче, но и решает ряд проблем

  • Необязательные параметры остаются необязательными
  • Имена аргументов сохраняются
  • Работает для любого количества аргументов

Образец:

type WithOptional = ReplaceReturnType<(n?: number)=> string, Promise<string>>;
let x!: WithOptional; // Typed as (n?: number) => Promise<string>
x() // Valid
x(1); //Ok

оригинал


Для хорошего решения вам понадобятся переменные типы, но пока этот ответ дает работоспособное решение. (Размещение его здесь как типа там используется как часть решения другого вопроса).

Основная идея заключается в том, что мы извлечем типы параметров и перекомпоновываем сигнатуру функции с новым возвращаемым типом. У этого подхода есть несколько недостатков:

  1. Имена параметров не сохраняются
  2. Необязательные параметры не обрабатываются хорошо
  3. Работает только для определенного количества аргументов (но может быть добавлено больше при необходимости)

Могут быть и другие проблемы, но в зависимости от вашего варианта использования это может быть достаточно хорошим решением, пока система типов не решит этот вариант использования.

type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type ReplaceReturnType<T, TNewReturn> = T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
    IsValidArg<J> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => TNewReturn :
    IsValidArg<I> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => TNewReturn :
    IsValidArg<H> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => TNewReturn :
    IsValidArg<G> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => TNewReturn :
    IsValidArg<F> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F) => TNewReturn :
    IsValidArg<E> extends true ? (a: A, b: B, c: C, d: D, e: E) => TNewReturn :
    IsValidArg<D> extends true ? (a: A, b: B, c: C, d: D) => TNewReturn :
    IsValidArg<C> extends true ? (a: A, b: B, c: C) => TNewReturn :
    IsValidArg<B> extends true ? (a: A, b: B) => TNewReturn :
    IsValidArg<A> extends true ? (a: A) => TNewReturn :
    () => TNewReturn
) : never

Проблема при использовании с необязательными параметрами заключается в том, что необязательный параметр становится обязательным (и имеет type A | undefined):

type WithOptional = ReplaceReturnType<(n?: number)=> string, Promise<string>>;

let x!: WithOptional;
x(); //invalid
x(undefined);
x(1);
Другие вопросы по тегам