I am attempting to create a generic which will give me information about all class methods return types ( fields, but they are not that important).
Let's say I have created such a class:
class TestClass {
testField: string;
constructor() {
this.testField = 'test';
}
testMethod1(param: number): number {
return param;
}
testMethod2(param: string): string {
return param;
}
}
As the result, I would like to have such type:
interface TestClassMethodsNamesReturnType {
testMethod1: number;
testMethod2: string;
}
What I have tried so far
mapping types types inference:
Example 1
export type ClassMethodsNamesReturnType<T extends Function> = { [k in keyof T['prototype']]: ReturnType<T['prototype'][k]>; } const x: ClassMethodsNamesReturnType<TestClass> = { //... }
Error:
Type 'TestClass' is missing the following properties from type 'Function': apply, call, bind, prototype, and 5 more
mapping types and treat class as object:
Example 2
// I think here I can have problems to decide is the key of my class is a field or method // what can be a problematic to decide what I should use (typeof T[K] or ReturnType<typeof T[K]>) export type ClassMethodsNamesReturnType<T extends Record<string, T[K]>, K extends keyof T> = { [K]: T[K]; }
Error:
A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type
'K' only refers to a type, but is being used as a value here
Do you have any hints or ideas how can I achieve this?
CodePudding user response:
You can do this via mapped types although you would need to do a conditional filter using the as
clause in the mapped type. Then, check if the value extends Function
and then return never
if it does not so it will not be mapped over. Here's the full code:
class TestClass {
testField: string;
constructor() {
this.testField = 'test';
}
testMethod1(param: number): number {
return param;
}
testMethod2(param: string): string {
return param;
}
}
type FnReturns<T> = { [K in keyof T as T[K] extends Function ? K : never]: ReturnType<T[K] extends (...args: any[]) => any ? T[K] : never> };
// Correctly passes:
const foo: FnReturns<InstanceType<typeof TestClass>> = {
testMethod1: 23,
testMethod2: "hey",
}
// correctly fails:
const fail: FnReturns<InstanceType<typeof TestClass>> = {}
Also note how I make use of the InstanceType
of typeof TestClass
to get the instance methods and properties of TestClass
.