Эта проблема состоит из двух частей, первая - правильная подпись функции.Мы хотим, чтобы функция взяла ключ к some.Api
и вернула значение того же типа, что и исходный ключ.Для этого нам понадобится дополнительный параметр типа K
, который расширяет keyof some.Api
, и мы будем использовать запрос типа, чтобы сообщить компилятору, что возвращаемый результат совпадает с типом переданного поля (some.Api[K]
)
declare namespace some {
type Api = {
foo(n: number): string;
bar(s: string, n: number) : string
}
function api(): Api;
}
function wrapCallByName<K extends keyof some.Api>(functionName: K) : some.Api[K] {
return ((...args: any[]) => {
try {
return (some.api()[functionName] as any)(...args);
} catch (error) {
throw error;
}
}) as any;
}
const foo = wrapCallByName('foo')
foo(1) //returns string
const bar = wrapCallByName('bar')
bar('', 1) //returns string
Playground
Как вы видите, в приведенной выше реализации есть много утверждений типа для любого.Это потому, что есть несколько проблем.Во-первых, индексный доступ к API приведет к не вызываемому объединению всех полей в API.во-вторых, функция, которую мы возвращаем, не совместима ни с одним полем API.Чтобы обойти это, мы можем использовать дополнительное косвенное обращение, которое заставит компилятор рассматривать значения объекта как функции с сигнатурой (... a: any[]) =>any
.Это исключит необходимость каких-либо утверждений.
declare namespace some {
type Api = {
foo(n: number): string;
bar(s: string, n: number): string
}
function api(): Api;
}
function wrapBuilder<T extends Record<keyof T, (...a: any[]) => any>>(fn: () => T) {
return function <K extends keyof T>(functionName: K): T[K] {
return ((...args: any[]) => {
try {
return fn()[functionName](...args);
} catch (error) {
throw error;
}
});
}
}
const wrapCallByName = wrapBuilder(() => some.api());
const foo = wrapCallByName('foo');
foo(1); //returns string
const bar = wrapCallByName('bar');
bar('', 1); //returns string
Детская площадка
Я упомянул эту замечательную реализацию без утверждений, потому что ваш вопрос специально его опровергает, лично мне будет достаточно комфортно с первой версией, особенно еслиВаш API выставляет только функции.Функция-обертка должна быть написана только один раз, и я не могу вспомнить ситуацию, когда утверждения вызовут проблему, тем более важной частью является то, что публичная подпись правильно перенаправляет типы.