使用参数调用对象的成员

时间:2019-01-10 13:01:19

标签: typescript

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

可以键入以上内容,以便执行以下操作:

  • methodNameobject的键。
  • object[methodName]是可调用的,其args为...args
  • whatever(object, methodName, args)的返回类型是object[methodName](...args)的返回类型。

我能找到的最接近的东西是function.apply的定义,但与上面的定义并不完全相同。

8 个答案:

答案 0 :(得分:30)

认为这可以解决问题:

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!

答案 1 :(得分:3)

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])

Playground link

Dictionary类型允许使用T[P]

ParametersReturnType类型被烘焙到TypeScript中。

MethodNames类型提取其值可分配给Function类型的所有键。数组类型需要特殊情况。

清单

  • 方法名称已验证? ✅
  • 参数是否经过类型检查? ✅
  • 返回类型正确吗? ✅

答案 2 :(得分:2)

返回类型是否始终与someObject [methodName]相同?

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

然后您可以执行此操作。

答案 3 :(得分:2)

这应该做到。这样会检查methodName以及每个args

(注意:不完美,可以进行一些改进;例如unknown-> any

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]);

答案 4 :(得分:1)

怎么样?

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

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

答案 5 :(得分:1)

到目前为止,我能想到的最接近的结果是


    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")

可以正确检查作为对象一部分的键。 虽然仍然缺少返回类型和args的类型推断。如果找到更好的解决方案,我将更新答案。

答案 6 :(得分:1)

最安全类型的选项是使用Parameters获取函数的参数类型(以使参数为安全类型),ReturnType返回a的结果。功能。

您还需要添加一个类型参数来捕获传入的实际键,以便我们可以获取函数的实际类型(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

答案 7 :(得分:0)

可以满足所有条件,并且不需要类型断言。

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的属性,这是JavaScript支持的全部色域。