Проблема

Для заданной функции Fn и типа A, реализуйте тип, который принимает Fn первым параметром, A вторым параметром и создает новый тип G, который вернёт функцию Fn, но с добавлением нового параметра типа A в конец параметров. Например:

type Fn = (a: number, b: string) => number;

// expected be (a: number, b: string, x: boolean) => number
type Result = AppendArgument<Fn, boolean>;

Решение

Интересная проблема, для решения которой нужно проявить немного креатива.

Мы начнём с выведения типов параметров входной функции и типа возврата. Условные типы помогут в этом. Как только мы выведем типы, вернём новую сигнатуру функции, которая копирует входную, но с использованием наших выведенных типов.

type AppendArgument<Fn, A> = Fn extends (args: infer P) => infer R
  ? (args: P) => R
  : never;

Очевидно, это решение ещё не готовое. Почему? Потому что, мы проверяем что тип Fn присваиваемый к типу функции с единственным параметром args. Это неправда, у нас может быть больше одного параметра или не быть вовсе.

Чтобы починить эту проблему, воспользуемся ... оператором:

type AppendArgument<Fn, A> = Fn extends (...args: infer P) => infer R
  ? (args: P) => R
  : never;

Теперь, наше условие в условном типе срабатывает и мы попадаем в ветку, где у нас есть P (типы параметров функции) и R (тип возврата функции).

Но, у нас всё ещё проблема. Тип параметр P это кортеж с параметрами внутри. Нам же нужно интерпретировать этот кортеж как отдельные параметры, а не как один.

Применяя вариативные типы к этому кортежу, мы можем разложить его на отдельные параметры:

type AppendArgument<Fn, A> = Fn extends (...args: [...infer P]) => infer R
  ? (args: P) => R
  : never;

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

type AppendArgument<Fn, A> = Fn extends (...args: [...infer P]) => infer R
  ? (...args: [...P]) => R
  : never;

На этом этапе, тип принимает входную функцию и возвращает такую же функцию, используя выведенные типы. Всё, что нам остается сделать, это добавить к кортежу с параметрами новый параметр A.

type AppendArgument<Fn, A> = Fn extends (...args: [...infer P]) => infer R
  ? (...args: [...P, A]) => R
  : never;

Что почитать