Вызов члена объекта с аргументами

function whatever(object, methodName, args) {
  return object[methodName](...args);
}

Можно ли набрать вышеперечисленное, чтобы было выполнено следующее:

  • methodName - это ключ object.
  • object[methodName] может быть вызван, а его аргументы ...args.
  • Тип возврата whatever(object, methodName, args) является типом возврата object[methodName](...args).

Самое близкое, что я могу найти, это определение function.apply, но оно не совсем то же, что и выше.

Ответ 1

Я думаю, что это делает трюк:

function callMethodWithArgs<
  M extends keyof T,
  T extends { [m in M]: (...args: Array<any>) => any },
  F extends T[M]
>(obj: T, methodName: M, args: Parameters<F>) {
  return obj[methodName](...args) as ReturnType<F>;
}

Требуется TS 3, хотя!

Ответ 2

type Dictionary = { [key: string]: any }

type MethodNames<T extends Dictionary> = T extends ReadonlyArray<any>
  ? Exclude<keyof [], number>
  : { [P in keyof T]: T[P] extends Function ? P : never }[keyof T]

function apply<T extends Dictionary, P extends MethodNames<T>>(
  obj: T,
  methodName: P,
  args: Parameters<T[P]>
): ReturnType<T[P]> {
  return obj[methodName](...args);
}

// Testing object types:
const obj = { add: (...args: number[]) => {} }
apply(obj, 'add', [1, 2, 3, 4, 5])

// Testing array types:
apply([1, 2, 3], 'push', [4])

// Testing the return type:
let result: number = apply(new Map<number, number>(), 'get', [1])

= T extends ReadonlyArray ? Exclude : { [P in keyof T]: T[P] extends Function? P: never }[keyof T] function apply>( obj: T, methodName: P, args: Parameters ): ReturnType { return obj[methodName](...args); } //Testing object types: const obj = { add: (...args: number[]) => {} } apply(obj, 'add', [1, 2, 3, 4, 5]) //Testing array types: apply([1, 2, 3], 'push', [4]) //Testing the return type: let result: number = apply(new Map(), 'get', [1]) rel="nofollow noreferrer">Ссылка на игровую площадку

Тип Dictionary позволяет использовать T[P].

Parameters и ReturnType включаются в TypeScript.

Тип MethodNames извлекает любые ключи, значение которых MethodNames типу Function. Типы массивов требуют особого случая.

контрольный список

  • Имя метода подтверждено? ✅
  • Аргументы проверены? ✅
  • Тип возврата правильный? ✅

Ответ 3

Является ли тип возвращаемого значения всегда одинаковым для someObject [methodName]?

function whatever<O extends {[key: string]: (...args) => R}, R>(object: O, methodName: keyof O, ...args: any[]): R {
  return object[methodName](...args);
}

Тогда вы могли бы сделать это.

Ответ 4

Это должно сделать это. Это проверяет метод methodName а также каждый args.

(Примечание: не идеально, может быть сделано некоторое уточнение; например, unknownany)

type ArgumentsType<T> = T extends (...args: infer A) => any ? A : never;

function whatever<
  T extends object,
  TKey extends keyof T,
  TArgs extends ArgumentsType<T[TKey]>
>(
  object: T,
  methodName: T[TKey] extends ((...args: TArgs) => unknown) ? TKey : never,
  args: TArgs
): T[TKey] extends ((...args: TArgs) => unknown) ? ReturnType<T[TKey]> : never {
  const method = object[methodName];
  if (typeof method !== 'function') {
    throw new Error('not a function');
  }
  return method(...args);
}

interface Test {
  foo: (a: number, b: number) => number;
  bar: string;
}

const test: Test = {
  foo: (a, b) => a + b,
  bar: 'not a function'
};

const result = whatever(test, 'foo', [1, 2]);

Ответ 5

Как насчет этого?

function whatever(someObject: { [key: string]: Function}, methodName: string, args: any[]) {
    return someObject[methodName](...args);
}

whatever({ func1: (args) => (console.log(...args)) }, 'func1', [1])

Ответ 6

Ближайший результат, о котором я могу думать, таков:


    function whatever<T extends object>(object: T, methodName: keyof T, args: any) {
        const func = object[methodName]
        if (typeof func === "function") {
            return func(...[args])
        }
    }

    const obj = {
      aValue: 1,
      aFunc: (s: string) => "received: " + s
    }

    whatever(obj, "aFunc", "blabla")

, который правильно проверяет ключ на предмет принадлежности к объекту. Вывод типа для возвращаемого типа и аргументов все еще отсутствует. Я обновлю ответ, если найду лучшее решение.

Ответ 7

Опция, которая является наиболее безопасной по типу, состоит в том, чтобы использовать Parameters для получения типов параметров функции (для того, чтобы аргументы были безопасными для типов), ReturnType для возврата результата функции.

Вам также необходимо добавить параметр типа для захвата фактического ключа, переданного, чтобы мы могли получить фактический тип функции (T[K] (

function whatever<T extends Record<string, (...a: any[])=> any>, K extends keyof T> (someObject: T,  methodName: K, ...args: Parameters<T[K]>) : ReturnType<T[K]>{
    return someObject[methodName](...args);
}

whatever({ func1: (args: number[]) => (console.log(...args)) }, 'func1', [1])
whatever({ func1: (args: number[]) => (console.log(...args)) }, 'func1', ["1"]) // err

Ответ 8

Итак, вы соответствуете всем критериям и не нуждаетесь в утверждениях типа.

type AnyFunction = (...args: any[]) => any;

function whatever<
  T extends Record<PropertyKey, AnyFunction>,
  K extends keyof T,
  A extends Parameters<T[K]>
>(object: T, methodName: K, args: A): ReturnType<T[K]> {
    return object[methodName](...args);
}

Тип Parameters является частью стандартной библиотеки начиная с TypeScript 3.1. Если вы используете старую версию, создайте ее самостоятельно:

type Parameters<T extends (...args: any[]) => any> =
  T extends (...args: infer P) => any
    ? P
    : never;

Использование типа PropertyKey вместо string позволяет использовать свойства типа string | number | symbol string | number | symbol string | number | symbol, который является полной гаммой, поддерживаемой JavaScript.