从索引的泛型类型获取类方法参数类型

huangapple go评论65阅读模式
英文:

Getting class method argument types from indexed generic type

问题

class Example {
    methodA(param1: string, param2: number) {}
    methodB(): string {}
    methodC(param1: boolean, param2: number): number {}
    prop1: string = '';
}

function call<K extends keyof Example>(functionName: K, ...args: Parameters<Example[K]>): ReturnType<Example[K]> {
    // Your implementation here...
}

你的代码看起来基本上是正确的,但是你可能需要确保 call 函数的实现与参数类型相匹配。在你的 call 函数中,Parameters<Example[K]>ReturnType<Example[K]> 应该与指定的方法匹配,否则会导致类型错误。

如果你在 call 函数的实现中正确地使用了 args,并且返回了与指定方法的返回类型相匹配的值,那么你的代码应该可以正常工作。如果你仍然遇到类型错误,请确保 call 函数的实现与方法的预期签名相匹配。

英文:

I have a class with some functions as well as a non-function property:

class Example {
    methodA(param1: string, param2: number) {}
    methodB(): string {}
    methodC(param1: boolean, param2: number): number {}
    prop1: string = &#39;&#39;;
}

I want to write a method that would receive the class method name as a parameter and the corresponding method's arguments as the remaining parameters and also return the same data type as the specified class method. It would look like this:

call(&#39;methodA&#39;, &#39;a string&#39;, 123); // If the method has a void return type
let value = call(&#39;methodC&#39;, true, 456) // If the method has a non-void return type;

I can fetch the method names using keyof Example as follows:

function call&lt;K extends keyof Example&gt;(functionName: K) {}

However, I am stuck at getting the corresponding method arguments and return type. My attempt has been as follows:

function call&lt;K extends keyof Example&gt;(functionName: K, ...args: Parameters&lt;Example[K]&gt;): ReturnType&lt;Example[K]&gt;

When I do this, I get an error stating that Type &#39;Example[K]&#39; does not satisfy the constraint &#39;(...args: any) =&gt; any&#39;.

When I use the indexed type on its own and without using the rest parameters

function call&lt;K extends keyof Example&gt;(functionName: K, args: Example[K])...

I can see that the correct function signature is expected as the args parameters, so I assumed that this function type satisfy the constraint expected by the Parameters<> utlity type. I have a similar issue with the ReturnType utility type.

How could I get Example[K] to work as (...args: any) =&gt; void?

答案1

得分: 0

以下是翻译好的部分:

你想要获取 Example 中属性值可调用的键。没有内置的实用程序类型可以为您完成此操作(请参阅 microsoft/TypeScript#48992 相关的功能请求),但您可以通过多种方式自己定义。这是一种方式,使用 键重映射

type KeysMatching<T extends object, V> = keyof {
  [K in keyof T as T[K] extends V ? K : never]: any
}

因此,KeysMatching<T, V> 将为您提供仅适用于 T 属性值可分配给 V 的那些键。因此,您可以像这样获取 Example 中可调用的键:

type CallableKeys = KeysMatching<Example, (...args: any) => any>;
// type CallableKeys = "methodA" | "methodB" | "methodC"

现在,您可以根据 CallableKeys 定义 call()

declare function call<K extends CallableKeys>(
    functionName: K, ...args: Parameters<Example[K]> // okay
): ReturnType<Example[K]>;

并且错误会消失。

这种方法的优点是,如果将更多的非函数属性添加到 Example 类中,它会自动更新。

代码示例链接

英文:

You want to get just the keys of Example whose property values are callable. There is no built-in utility type that does this for you (see microsoft/TypeScript#48992 for the relevant feature request) but you can define your own in a number of ways. Here's one way, using key remapping:

type KeysMatching&lt;T extends object, V&gt; = keyof { 
  [K in keyof T as T[K] extends V ? K : never]: any 
}

So KeysMatching&lt;T, V&gt; will give you just those keys of T whose property values are assignable to V. And so you can get just the callable keys of Example like this:

type CallableKeys = KeysMatching&lt;Example, (...args: any) =&gt; any&gt;;
// type CallableKeys = &quot;methodA&quot; | &quot;methodB&quot; | &quot;methodC&quot;

Now you can then define call() in terms of CallableKeys:

declare function call&lt;K extends CallableKeys&gt;(
    functionName: K, ...args: Parameters&lt;Example[K]&gt; // okay
): ReturnType&lt;Example[K]&gt;;

and the error goes away.

This approach has the advantage of automatically updating if more non-function properties are added to the Example class.

Playground link to code

答案2

得分: 0

我已找到解决我的问题的方法,问题在于我使用的类还包含公共变量/属性,这些变量/属性会被keyof Example类型捕捉到。当在Parameters&lt;&gt;类型中使用它时,会引发错误,因为并非所有类属性都可调用。

解决方法是将keyof Example包裹在Exclude&lt;&gt;实用类型中,并指定需要排除的键。

例如,如果类声明如下:

class Example {
    public prop1: string = '';

    methodA(param1: string, param2: number) {}
    methodB(): string {}
    methodC(param1: boolean, param2: number): number {}
}

然后,可调用的键可以使用Exclude&lt;keyof Example, 'prop1'&gt;来获取,结果是Parameters&lt;Exclude&lt;keyof Example, 'prop1'&gt;&gt;是有效的。

英文:

I have found the solution to my issue, the problem was that the class I was using also contained public variables/properties and these are picked up by the keyof Example type. When using this in a Parameters&lt;&gt; type, it throws an error because not all the class properties are callable.

The solution was to enclose the keyof Example inside an Exclude&lt;&gt; utility type and specify which keys need to be excluded.

For example, if the class was declared as follows:

class Example {
    public prop1: string = &#39;&#39;;

    methodA(param1: string, param2: number) {}
    methodB(): string {}
    methodC(param1: boolean, param2: number): number {}
}

then callable keys can be obtained using Exclude&lt;keyof Example, &#39;prop1&#39;&gt; resulting in Parameters&lt;Exclude&lt;keyof Example, &#39;prop1&#39;&gt;&gt; being valid.

huangapple
  • 本文由 发表于 2023年6月8日 04:27:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76426896.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定