The title may be misleading, but I don't really know how to express my problem.
I want to provide a generic class (or just the class body) as a function argument. Then the provided class should infer its generics.
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
}
}
Usage:
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
As an (ugly) "workaround" I found a way to provide all class methods separately and then build a class from it. Something like this:
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);
}
}
But that's really ugly.
Basically I really just want to provide the class body to the build
method. And then this method will create a class from it, call wrap
and return.
Is there any way to do this?
EDIT: Another try to explain my problem:
At the moment I have to use the code like this:
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
}
}
})
As you can see I have to specify the generics of AbstractClass
. I don't want to specify the type, because builder
already knows the type.
I just want to provide the body of the class without specifying the generic type again. Something like this:
builder.build(class extends AbstractClass<infer the type with magic!> {
...
getResult() {
return { this.data.foo }
}
})
Or this:
builder.build(class {
...
getResult() {
return { this.data.foo }
}
})
I just had a good idea. Which adds a bit of unused code, but fulfills the type inference.
The idea is pretty simple: Just use
typeof
!Now it's possible to use the
type: INT
like this:I'm not sure if there's any better solution.