标题可能会引起误解,但我真的不知道该如何表达我的问题。
我想提供一个泛型类(或只是类主体)作为函数参数。然后,所提供的类应推断其泛型。
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 }
}
})
答案 0 :(得分:2)
我不是100%肯定我了解您想要什么,或者您想要什么是可行的。但是这里有一些想法...
首先,您所说的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
}
}
然后,当您调用它时...首先,您无法在TypeScript中更改值的类型,因此您无法重新分配builder
。但是您仍然可以进行链接(或为中间值指定新名称)。我将其链接。在这里:
const builder = new Builder<{ foo: number }>();
const ctor = builder.withBar().build(class {
foo = 10;
bar = "you";
});
这可行。但是请注意,您确实需要在匿名类主体中定义foo
和bar
。如果您不键入TypeScript,将会感到不高兴。因此,当您“拥有”访问权限时,可能并没有您想要的方便?
无论如何,如果还不够,请添加更多详细信息,也许我可以提供更多帮助。祝你好运!
答案 1 :(得分:0)
我只是有一个好主意。这会添加一些未使用的代码,但会执行类型推断。
这个想法很简单:只需使用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 }
}
})
我不确定是否有更好的解决方案。