linbudu599 / TS-AMA

Show me your typescript playground link.
MIT License
20 stars 1 forks source link

Extract all methods' parameters from a class to a union type #3

Open 1zumii opened 4 months ago

1zumii commented 4 months ago
class TestClass {
    private field: string = "";

    func1(a: number): string {
        return a.toString();
    }

    func2(b: boolean): boolean[] {
        return [b, true, false];
    }
}

type MethodParams<Class extends abstract new (...args: any) => any, Member = Class[keyof Class]> =
    Member extends (...args: any[]) => any
        ? Parameters<Member>
        : never;

type Result = MethodParams<typeof TestClass>; // never

but Result inferred as never, not number | boolean as expected

linbudu599 commented 4 months ago

Updated code sample:

class TestClass {
  private field: string = '';

  func1(a: number): string {
    return a.toString();
  }

  func2(b: boolean): boolean[] {
    return [b, true, false];
  }
}

type MethodParams<
  Class extends abstract new (...args: any) => any,
  Members = InstanceType<Class>
> = {
  [K in keyof Members]: Members[K] extends (...args: any[]) => any
    ? Parameters<Members[K]>[0]
    : never;
}[keyof Members];

type Result = MethodParams<typeof TestClass>;

Or use instance type directly:

// Or extends object if you want this tool type more generic
type MethodParams<Class extends InstanceType<typeof TestClass>> = {
  [K in keyof Class]: Class[K] extends (...args: any) => any
    ? Parameters<Class[K]>[0]
    : never;
}[keyof Class];

type Result = MethodParams<TestClass>;
1zumii commented 4 months ago

🤔 i notice that InstanceType just infer the matched type, when i navigate into TS sourcecode

**
 * Obtain the return type of a constructor function type
 */
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;

still confused at this part

Class[keyof Class], represents the instance type, equals with InstanceType<Class> here.

linbudu599 commented 4 months ago
class TestClass {
  field: string = '';
}

type A = typeof TestClass; // The Constructor Type, only contains 'prototype' property ({ prototype: TestClass })

type B = TestClass; // The Instance Type

type Ins_A = InstanceType<A>; // InstanceType<Constructor Type> = Instance Type

type Z = A[keyof A]; // Equals with A['prototype'], which also equals with Instance Type