TypeScript:通过提供 class body 创建通用匿名 class
TypeScript: Create generic anonymous class by providing class body
标题可能有误导性,但我真的不知道如何表达我的问题。
我想提供一个通用的 class(或只是 class body)作为函数参数。然后提供的 class 应该推断出它的泛型。
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
作为一个(丑陋的)"workaround",我找到了一种方法来单独提供所有 class 方法,然后从中构建一个 class。像这样:
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);
}
}
但这真的很难看。
基本上我真的只想提供 class body 到 build
方法。然后此方法将从中创建一个 class,调用 wrap
和 return.
有什么办法吗?
编辑:再次尝试解释我的问题:
目前我必须使用这样的代码:
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
已经知道类型。
我只想提供 class 的 body 而无需再次指定通用类型。像这样:
builder.build(class extends AbstractClass<infer the type with magic!> {
...
getResult() {
return { this.data.foo }
}
})
或者这个:
builder.build(class {
...
getResult() {
return { this.data.foo }
}
})
我不是 100% 确定我理解您想要的,或者您想要的是可行的。但这里有一些想法...
首先,您所谓的 MyClass<T>
似乎指的是 class 构造函数,其中 returns 是 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";
});
所以这行得通。但请注意,您确实需要在匿名 class 正文中定义 foo
和 bar
。如果您将它们排除在外,TypeScript 会感到不安。所以当你对他们做 "have access" 的时候,它可能没有你想要的那么方便?
无论如何,如果这还不够,请添加更多详细信息,也许我可以提供更多帮助。祝你好运!
我刚想到一个好主意。它添加了一些未使用的代码,但实现了类型推断。
想法很简单:只需使用 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 }
}
})
不知道有没有更好的解决办法。
标题可能有误导性,但我真的不知道如何表达我的问题。
我想提供一个通用的 class(或只是 class body)作为函数参数。然后提供的 class 应该推断出它的泛型。
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
作为一个(丑陋的)"workaround",我找到了一种方法来单独提供所有 class 方法,然后从中构建一个 class。像这样:
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);
}
}
但这真的很难看。
基本上我真的只想提供 class body 到 build
方法。然后此方法将从中创建一个 class,调用 wrap
和 return.
有什么办法吗?
编辑:再次尝试解释我的问题:
目前我必须使用这样的代码:
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
已经知道类型。
我只想提供 class 的 body 而无需再次指定通用类型。像这样:
builder.build(class extends AbstractClass<infer the type with magic!> {
...
getResult() {
return { this.data.foo }
}
})
或者这个:
builder.build(class {
...
getResult() {
return { this.data.foo }
}
})
我不是 100% 确定我理解您想要的,或者您想要的是可行的。但这里有一些想法...
首先,您所谓的 MyClass<T>
似乎指的是 class 构造函数,其中 returns 是 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";
});
所以这行得通。但请注意,您确实需要在匿名 class 正文中定义 foo
和 bar
。如果您将它们排除在外,TypeScript 会感到不安。所以当你对他们做 "have access" 的时候,它可能没有你想要的那么方便?
无论如何,如果这还不够,请添加更多详细信息,也许我可以提供更多帮助。祝你好运!
我刚想到一个好主意。它添加了一些未使用的代码,但实现了类型推断。
想法很简单:只需使用 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 }
}
})
不知道有没有更好的解决办法。