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<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 会很不高兴。因此,尽管您确实“可以访问”它们,但它可能并不像您想要的那么方便?
无论如何,如果这还不够,请添加更多细节,也许我可以提供更多帮助。祝你好运!
【讨论】:
谢谢!!我猜这很接近。现在的问题是clazz
必须扩展另一个类,它提供泛型...builder.build(class extends AbstractClass<T> ... )
。而这个T
就是问题所在。我不想写extends AbstractClass<T>
,因为我必须再次指定泛型类型。还有什么想法吗? :)
通常我必须使用build(class extends AbstractClass< foo: number, bar: string> ... )
。但是Builder
已经知道关于泛型的一切。虽然我想以某种方式摆脱extends AbstractClass< foo: number, bar: string>
。 ...EDIT...
也许这可以使用 mixins 来完成?
AbstractClass<T>
的定义是什么?应该是AbstractClass<T>
扩展T
吗?还是别的什么?
它不扩展T
。 T
用作类属性和方法的参数。就像这样:class AbstractClass<T> 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:通过提供类体创建通用匿名类的主要内容,如果未能解决你的问题,请参考以下文章