TypeScript: функция высшего порядка вычисляет возвращаемый тип функции обратного вызова без его явного указания
Как typescript'у удаётся вычислить возвращаемый тип функции wrapper, если я не передаю никаких параметров-типов в дженерик? TS Playground
function wrapper<T>(cb: () => T) {
return cb();
}
const result = wrapper(() => 'anything'); // string
const result1 = wrapper(() => 1); // number
Как называется этот приём и где можно об этом почитать? Есть ли более правильный способ написания подобной функции(без ненужного дженерика)?
Ответы (1 шт):
Типовые параметры ("дженерики") функций могут быть выведены из переданных аргументов, что здесь и происходит. ТС сопоставляет тип аргумента функции с типом аргумента в выражении её вызова и пытается вывести тип для <T>. В хендбуке есть соответствующий раздел для ознакомления. К сожалению, у TS отсутствует официальная спецификация и нормальная документация, поэтому изучить механизм вывода типов более детально можно только в пул реквестах, ишью и исходниках на гитхабе.
"Более правильного" способа написать эту функцию не существует, без <T> здесь не обойтись. Вас не должно смущать что он явно не указывается при вызове. Типовой параметр позволяет подставлять типы при вызове, а не при определении, что даёт возможность выстраивать зависимости между типами в сигнатуре функции. Соответственно, если такие зависимости вам нужны, вы должны написать дженерик.
В вашем случае возвращаемый тип функции зависит от типа её аргумента.
Пример:
declare function f(arg: unknown): typeof arg
declare function g<T>(arg: T): typeof arg
const a = f(1)
const b = g(1)
Поскольку тип аргумента функции f задан при определении, typeof arg всегда будет unknown. Но для функции g он будет вычисляться каждый раз при вызове, и typeof arg будет возвращать выведенный тип. Дженерик здесь убрать нельзя.
Если же заменить тип возвращаемого значения в этих функция, например, на void, то зависимостей от типа аргумента не останется. В таком случае нам не будет важно в какой именно момент определился тип аргумента, поэтому дженерик будет излишним.
declare function f(arg: unknown): void
declare function g<T>(arg: T): void
f(1)
g(1)