Append Argument
Завдання
Для даної функції 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>;
Розв’язок
Цікава задача! Тут нам знадобляться виведення типів, варіативні кортежі (variadic tuple types), умовні типи - багато цікавих речей.
Почнемо з виведення аргументів функції та типу, що вона повертає. Умовні типи допоможуть нам з цим. Коли типи виведені ми можемо повернути власну сигнатуру функції, що копіює вхідну:
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;
Тепер умова в умовному типі буде істинна, отже буде використана гілка “true” з
аргументами функції P
та поверне тип R
. Хоча все ще є проблема. P
містить
кортеж з аргументами функції, але нам потрібно розкласти його на окремі
елементи.
Ми це можемо зробити застосувавши варіативні кортежі:
type AppendArgument<Fn, A> = Fn extends (...args: [...infer P]) => infer R
? (args: P) => R
: never;
Параметр P
містить те, що нам потрібно. Залишилось лише сконструювати нашу
нову функцію з виведених типів.
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;
Коментарі