Я создал простую карту с двумя функциями, которые принимают один аргумент «foo: string» и реализуют различные функции, которые возвращаются напрямую:
import { mapValues } from 'lodash';
const fun1 = (foo: string) => (name: string, surname: string) => {};
const fun2 = (foo: string) => (age: number) => {};
interface Actions {
fun1: typeof fun1;
fun2: typeof fun2;
}
const actions: Actions = { fun1, fun2 };
const mappedActions = mapValues(actions, action => action('bar'));
mappedActions.fun1(1);
С помощью mapValues я перебираю эти функции и вызываю их с некоторым общим параметром. Я бы ожидал, что это карта функций полуназываемый, назначенных в mappedActions карте, но проблема с типами все еще остается.
fun1 не разрешается как функция, которая принимает (name: string, surname: string) аргументов, но все же является функцией верхнего уровня.
Как мне это составить, чтобы TypeScript правильно разрешал типы?
@JonasWilms Да, action('bar') разрешается правильно, поэтому требуется один строковый параметр. Передача чего-либо еще дает ошибку.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я скопировал ваш фрагмент в CodeSandbox и проанализировал функцию Lodash mapValues(), и я думаю, что типы для mapValues() не соответствуют вашей цели.
mapValues<T extends object, TResult>(obj: T | null | undefined, callback: ObjectIterator<T, TResult>): { [P in keyof T]: TResult };
Как видите, функция mapValues принимает объект (T extends object) и для каждого ключа этого объекта присваивает TResult, что делает это:
mapValues<Actions, ((name: string, surname: string) => void) | ((age: number) => void)>(obj: Actions, callback: ObjectIterator<Actions, ((name: string, surname: string) => void) | ((age: number) => void)>): {
Я пытался использовать lodash (особенно Лодаш/fp) с TypeScript и могу сказать, что не все работает идеально, также очень сложно создать типизацию универсальный для всех возможностей.
Хорошо, это имеет смысл. Так можно ли добиться этого в TS с помощью обычного цикла Object.values?
Я думаю, что нашел способ правильно разрешить типы:
import { mapValues } from 'lodash';
const fun1 = (foo: string) => (name: string, surname: string) => {};
const fun2 = (foo: string) => (age: number) => {};
type FunctionDictionary = { [id: string]: ((...args: any[]) => any) };
interface Actions extends FunctionDictionary {
fun1: typeof fun1;
fun2: typeof fun2;
}
const actions: Actions = { fun1, fun2 };
type ReturnTypeDict<T> = T extends { [id: string]: ((...args: any[]) => any) } ? {
[P in keyof T]: ReturnType<T[P]>;
} : never;
const map: (actions: Actions) => ReturnTypeDict<Actions> = actions => {
return mapValues(actions, action => action('bar')) as ReturnTypeDict<Actions>;
}
const mappedActions = map(actions);
mappedActions.fun1(1);
Ключевым здесь является условный тип ReturnType. Чтобы использовать его, вы должны создать свой собственный условный тип:
type ReturnTypeDict<T> = T extends { [id: string]: ((...args: any[]) => any) } ? {
[P in keyof T]: ReturnType<T[P]>;
} : never;
И теперь вы должны явно сказать, что ваш интерфейс расширяет нужный тип словаря:
type FunctionDictionary = { [id: string]: ((...args: any[]) => any) };
interface Actions extends FunctionDictionary {
fun1: typeof fun1;
fun2: typeof fun2;
}
Затем вы создаете функцию, которая правильно отображает данные:
const map: (actions: Actions) => ReturnTypeDict<Actions> = actions => {
return mapValues(actions, action => action('bar')) as ReturnTypeDict<Actions>;
}
Использует ли
mapValuesсопоставленный тип?