Я вижу несколько проблем с вашим примером кода; тот, который имеет непосредственное отношение к вашему вопросу, состоит в том, что T
является обобщенным c типом, который расширяет OnlyFunctions<Foo>
, что означает, что он вполне может иметь больше свойств, чем заявлено в OnlyFunctions<Foo>
, благодаря структурному подтип работает в TypeScript (типы объектов в TypeScript открыты , а не точные ). Если T extends {foo: string}
, то T
может быть {foo: string, bar: number}
или {foo: string, baz: boolean}
. Таким образом, компилятор не может предположить, что известные свойства T
являются только функциями, даже если известные свойства OnlyFunctions<Foo>
являются только функциями.
Я бы предложил полностью удалить T
, так как это не делает вас что-нибудь хорошее в этих примерах (из этого ничего не следует сделать), и замените его на OnlyFunctions<Foo>
. Вы можете оставить K
.
После этого все еще есть несколько проблем ... неверная запятая, использование call
вместо apply
(или, возможно, использование args
вместо ...args
, в зависимости от того, как вы на это смотрите), и тот факт, что когда вы вызываете apply
или call
на Foo[K]
, компилятор бесполезно расширяет свой тип до объединения перегруженных функций, которые к сожалению не вызывается . Мое решение будет выглядеть следующим образом:
const invoke = <K extends keyof OnlyFunctions<Foo>>(
method: K,
...args: Parameters<Foo[K]>
): ReturnType<Foo[K]> => {
const fooMethod: { apply(f: Foo, args: Parameters<Foo[K]>): ReturnType<Foo[K]> } =
foo[method];
return fooMethod.apply(foo, args)
};
Здесь я привел компилятор к более полезному типу для foo[method]
, назначив его промежуточной переменной с именем fooMethod
, чей метод apply()
как известно, подходящего типа. Это компилируется и работает правильно:
console.log(invoke("a", 1, 2)) // 4
Хорошо, надеюсь, это поможет; удачи!
Детская площадка ссылка на код