TypeScript:通过提供类体创建通用匿名类

Posted

技术标签:

【中文标题】TypeScript:通过提供类体创建通用匿名类【英文标题】:TypeScript: Create generic anonymous class by providing class body 【发布时间】:2019-01-05 01:20:02 【问题描述】:

标题可能会误导,但我真的不知道如何表达我的问题。

我想提供一个泛型类(或只是类体)作为函数参数。然后提供的类应该推断出它的泛型。

class Builder<EXT, INT = EXT> 
    // some builder stuff, which changes the generic T
    withBar(): Builder<EXT, INT &  bar: string > 
      return this as any;
    

    // here's the problem:
    build(clazz: MyClass<INT>): MyClass<EXT> 
       return wrap(clazz); // this method already exists and works
    

用法:

const builder = new Builder< foo: number >();
// EXT =  foo: number , INT =  foo: number 

builder = builder.withBar();
// EXT =  foo: number , INT =  foo: number, bar: string 

builder.build(class  /* here I should be able to access this.foo and this.bar */ );
// Here I just want to provide the class body,
// because I don't want to type all the generics again.
// I don't want to specify `MyClass<?>`,
// because the correct type is already specified within the Builder

作为一个(丑陋的)“解决方法”,我找到了一种方法来分别提供所有类方法,然后从中构建一个类。像这样的:

class Builder<EXT, INT = EXT> 
    build(args: method1?: any, method2?: any): MyClass<EXT> 
       class Tmp 
         method1() 
           return args.method1 && args.method1(this);
         
         method2(i: number) 
           return args.method2 && args.method2(this);
         
       

       return wrap(Tmp);
    

但这真的很难看。

基本上我真的只想为build 方法提供类主体。然后这个方法会从中创建一个类,调用wrap并返回。

有什么办法吗?

编辑:另一个尝试解释我的问题:

目前我必须使用这样的代码:

builder.build(class extends AbstractClass< foo: number, bar: string > 
    private prop: string;
    init() 
      this.prop = this.data.foo   // provided by AbstractClass
          ? 'foo'
          : 'bar'
    
    getResult() 
      return 
        foo: this.prop,
        bar: this.data.bar  // provided by AbstractClass
      
    
)

如您所见,我必须指定AbstractClass 的泛型。我不想指定类型,因为builder 已经知道类型。

我只想提供类的主体,而无需再次指定泛型类型。像这样的:

builder.build(class extends AbstractClass<infer the type with magic!> 
    ...
    getResult() 
        return  this.data.foo 
    
)

或者这个:

builder.build(class 
    ...
    getResult() 
        return  this.data.foo 
    
)

【问题讨论】:

【参考方案1】:

我不是 100% 确定我理解您想要什么,或者您想要什么是可行的。但这里有一些想法......

首先,您调用的MyClass&lt;T&gt; 似乎是指返回T 的类构造函数。这种类型可以用这样的构造函数签名来表示:

type Constructor<T> = new (...args: any[]) => T;

所以Builder 应该是:

class Builder<EXT, INT = EXT> 
  // some builder stuff, which changes the generic T
  withBar(): Builder<EXT, INT &  bar: string > 
    return this as any;
  

  // here's maybe a solution:
  build(clazz: Constructor<INT>): Constructor<EXT> 
    return wrap(clazz) as any; // as any?  not sure
  

然后,当你调用它时......首先,你不能在 TypeScript 中改变值的类型,所以你不能重新分配builder。但是你仍然可以做一个链(或给中间值新名称)。我会把它锁起来。这里:

const builder = new Builder< foo: number >();

const ctor = builder.withBar().build(class 
  foo = 10;
  bar = "you";
);

所以这行得通。但请注意,您确实需要在匿名类主体中定义 foobar。如果你把它们排除在外,TypeScript 会很不高兴。因此,尽管您确实“可以访问”它们,但它可能并不像您想要的那么方便?


无论如何,如果这还不够,请添加更多细节,也许我可以提供更多帮助。祝你好运!

【讨论】:

谢谢!!我猜这很接近。现在的问题是clazz 必须扩展另一个类,它提供泛型...builder.build(class extends AbstractClass&lt;T&gt; ... )。而这个T 就是问题所在。我不想写extends AbstractClass&lt;T&gt;,因为我必须再次指定泛型类型。还有什么想法吗? :) 通常我必须使用build(class extends AbstractClass&lt; foo: number, bar: string&gt; ... )。但是Builder 已经知道关于泛型的一切。虽然我想以某种方式摆脱extends AbstractClass&lt; foo: number, bar: string&gt;...EDIT... 也许这可以使用 mixins 来完成? AbstractClass&lt;T&gt;的定义是什么?应该是AbstractClass&lt;T&gt; 扩展T 吗?还是别的什么? 它不扩展TT 用作类属性和方法的参数。就像这样:class AbstractClass&lt;T&gt; protected data: T; constructor(data: T) this.data = data; abstract init(): void; abstract getResult(): MyResult .... 我想使用Builder 提供以下内容:builder.build(class private prop: string; init() this.prop = this.data.foo ? 'foo' : 'bar' getResult() return foo: this.prop, bar: this.data.bar )。 (无用的例子,但你应该看看我想如何以及访问哪些属性)。顺便说一句:我会在 2 天内在这里添加一个大赏金:)【参考方案2】:

我刚刚想到了一个好主意。这增加了一些未使用的代码,但实现了类型推断。

这个想法很简单:只需使用typeof

class Builder<EXT, INT = EXT> 
    // some builder stuff, which changes the generic T
    withBar(): Builder<EXT, INT &  bar: string > 
      return this as any;
    

    build(clazz: (type: INT) => Constructor<INT>): MyClass<EXT> 
      return wrap(clazz(null as any) as any) as any;
    


interface Constructor<T> 
    new (data: T): any;

现在可以像这样使用type: INT

// here happens the "magic"
builder.build((type) => class extends AbstractClass<typeof type> 
    getResult() 
        return  this.data.foo 
    
)

我不确定是否有更好的解决方案。

【讨论】:

以上是关于TypeScript:通过提供类体创建通用匿名类的主要内容,如果未能解决你的问题,请参考以下文章

20165203《Java程序设计》第五周学习总结

匿名内部类了解一二

20165301 2017-2018-2 《Java程序设计》第五周学习总结

20165232 第五周学习总结

20165306 第五周学习任务

java中的匿名内部类