如何在TypeScript中传递类本身给函数并避免重复声明类型?
问题描述
我想把**类本身(而非类的实例)**作为参数传给函数,让函数能完成两个操作:1)调用该类的静态方法;2)实例化该类。
我已经实现了功能,但目前只能通过重新声明整个类结构为接口的方式实现,这导致类被重复定义——一次是class {}声明,另一次是作为函数参数的类型。
有没有办法避免这种重复,直接复用已有的类定义,不用额外重复定义类型?
以下是展示问题的可运行代码:
class Base { static id() { return { name: 'example', code: 'none' }; } static other1() { return 'example1'; } static other2() { return 'example2'; } constructor(a: number, b: number) { } } class A extends Base { static id() { return { ...super.id(), code: 'class-a' }; } static other1() { return 'example1A'; } constructor(a: number, b: number) { super(a, b + 1); } } class B extends Base { static id() { return { ...super.id(), code: 'class-b' }; } static other2() { return 'example2B'; } constructor(a: number, b: number) { super(a, b + 2); } } // 我必须在此重新声明所有静态函数。如何用自动从Base类生成的类型替换它? interface BaseClassAsParameter<T> { new(...args: any): T; id: () => any; other1: () => any; other2: () => any; // 如果我给Base添加另一个静态函数,我也必须在这里添加它。 } function testClass<T extends Base>(C: BaseClassAsParameter<T>): T { const { name, code } = C.id(); const other1 = C.other1(); const other2 = C.other2(); console.log(`Test ${name} code ${code}: ${other1} ${other2}`); const x = new C(100, 200); // 对x执行一些操作 return x; } testClass(A); // 应输出 "Test example code class-a: example1A example2" testClass(B); // 应输出 "Test example code class-b: example1 example2B"
解决方案
无需手动重复定义接口,直接使用TypeScript的typeof操作符获取类自身的类型(包含所有静态成员和构造函数),即可实现复用。
步骤1:删除冗余接口
移除手动定义的BaseClassAsParameter接口,避免重复维护静态成员的类型定义。
步骤2:修改函数参数类型
将函数参数类型改为typeof Base & (new(a: number, b: number) => T),既复用Base的静态成员类型,又约束构造函数返回子类实例。
修改后的完整代码
class Base { static id() { return { name: 'example', code: 'none' }; } static other1() { return 'example1'; } static other2() { return 'example2'; } constructor(a: number, b: number) { } } class A extends Base { static id() { return { ...super.id(), code: 'class-a' }; } static other1() { return 'example1A'; } constructor(a: number, b: number) { super(a, b + 1); } } class B extends Base { static id() { return { ...super.id(), code: 'class-b' }; } static other2() { return 'example2B'; } constructor(a: number, b: number) { super(a, b + 2); } } // 直接复用typeof Base,无需重复定义接口 function testClass<T extends Base>(C: typeof Base & (new(a: number, b: number) => T)): T { const { name, code } = C.id(); const other1 = C.other1(); const other2 = C.other2(); console.log(`Test ${name} code ${code}: ${other1} ${other2}`); const x = new C(100, 200); // 对x执行一些操作 return x; } testClass(A); // 输出 "Test example code class-a: example1A example2" testClass(B); // 输出 "Test example code class-b: example1 example2B"
进一步优化:自动同步构造函数参数
如果Base的构造函数参数有变动,手动写参数类型会增加维护成本,可使用ConstructorParameters<typeof Base>自动获取构造函数参数类型,让代码更健壮:
function testClass<T extends Base>(C: typeof Base & (new(...args: ConstructorParameters<typeof Base>) => T)): T { const { name, code } = C.id(); const other1 = C.other1(); const other2 = C.other2(); console.log(`Test ${name} code ${code}: ${other1} ${other2}`); const x = new C(100, 200); return x; }
原理说明
typeof Base获取Base类自身的类型,包含所有静态方法和构造函数的定义new(...args: ...) => T约束构造函数必须返回T类型的实例(T是Base的子类)- 两者结合后,参数C既可以调用Base的所有静态方法,又能实例化出子类对象
内容的提问来源于stack exchange,提问作者Malvineous
相关产品推荐
相关产品推荐

