代码之家  ›  专栏  ›  技术社区  ›  Benjamin M

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

  •  0
  • Benjamin M  · 技术社区  · 6 年前

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

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

    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 }
        }
    })
    
    2 回复  |  直到 6 年前
        1
  •  2
  •   jcalz    6 年前

    MyClass<T> 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
      }
    }
    

    builder

    const builder = new Builder<{ foo: number }>();
    
    const ctor = builder.withBar().build(class {
      foo = 10;
      bar = "you";
    });
    

    foo bar


        2
  •  0
  •   Benjamin M    6 年前

    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 }
        }
    })