我见过类似的问题,但没有回答我的具体问题。我想创建一个基类,用户应该能够以这样一种方式扩展我的基类中的方法返回基类的实例,当从派生类调用时返回派生类的实例。这个类是不可变的,有一堆可链接的方法。下面是一个类来说明这个问题:
class Pair<T> {
constructor(public left: T, public right : T) {}
setLeft(left: T) {
return new Pair(left, this.right)
}
setRight(right: T) {
return new Pair(this.left, right)
}
map<R>(fun: (t: T) => R) {
return new Pair(fun(this.left), fun(this.right));
}
}
扩展它的一个简单方法是扩展pair的原型,但我不认为这可以在另一个文件中完成(当我做interface Pair<T> { }
时,我得到一个错误告诉我"Import声明与pair ">的局部声明冲突。
另一个选择是创建一个派生类:
class MyPair<T> extends Pair<T> {
constructor(left: T, right: T) {
super(left, right)
}
swap() {
return new MyPair(this.right, this.left)
}
}
这里的问题是,一旦我调用Pair
方法,我将无法调用任何MyPair
方法,因为我将得到Pair
(做new MyPair(10, 20).setLeft(30).swap()
是不可能的)。
这个建议展示了如何使用activator函数来创建派生类的实例,如下所示:
class Pair<T, R extends Pair<T, R>> {
constructor(private activator: (left: T, right: T) => R, public left: T, public right : T) {}
setLeft(left: T) {
return this.activator(left, this.right)
}
setRight(right: T) {
return this.activator(this.left, right)
}
}
这似乎有点工作,但是activator使用类型T
作为参数的事实似乎有问题。当我在Pair
内部的方法中使用这两行时,会出现错误:
var hi = new Pair(this.activator, 10, 20);
var bye = this.activator(10, 20);
错误提示number
不能赋值给类型T
,所以似乎实参的类型被绑定到T
。我需要一种方法使我的通用激活剂保持通用,但这似乎是不可能的。如果我这样定义:
function foo<T>(fun: (t: T) => T): (t: T) => T {
return fun
}
然后写入
var bar = foo(a => a)
则bar
的类型将由(a: T) => T
变为(t: unknown) => unknown
。看来我又走到死胡同了?如果是这样,还有其他选择吗?
如何使用constructor
属性?
class Pair<T> {
['constructor']: new (...args: ConstructorParameters<typeof Pair>) => this
constructor(public left: T, public right: T) {}
setLeft(left: T): this {
return new this.constructor(left, this.right)
}
setRight(right: T): this {
return new this.constructor(this.left, right)
}
}
class MyPair<T> extends Pair<T> {
constructor(left: T, right: T) {
super(left, right)
}
swap() {
return new MyPair(this.right, this.left)
}
}
new MyPair(1, 2)
.setLeft(3)
.swap()
唯一的问题是它不会阻止你声明一个与(left: T, right: T) => this
不兼容的构造函数的类(例如,需要额外的参数)
操场上联系
另一种方法是使用Symbol.species
静态属性,这将允许子类自定义Pair
使用的"构造函数",但这意味着每个子类都必须相应地设置Symbol.species
:
interface PairConstructor<T> {
new (...args: ConstructorParameters<typeof Pair>): T
[Symbol.species]: PairConstructor<T>
}
class Pair<T> {
['constructor']: PairConstructor<this>
static [Symbol.species] = Pair
constructor(public left: T, public right: T) {}
setLeft(left: T): this {
return new this.constructor[Symbol.species](left, this.right)
}
setRight(right: T): this {
return new this.constructor[Symbol.species](this.left, right)
}
}
class MyPair<T> extends Pair<T> {
static [Symbol.species] = MyPair
constructor(left: T, right: T) {
super(left, right)
}
swap() {
return new MyPair(this.right, this.left)
}
}
操场上联系