如何在抽象 class return 子实例 classes 的打字稿中使用函数
How to have a function in an abstract class return instances of a child classes in typescript
是否可以有这样的伪代码:
abstract class AbstractParent {
protected value: number;
constructor(value: number) {
this.value = value;
}
...
public times(multiplicator: number) {
return new ???(value * multiplicator);
}
}
returns class Foo extends AbstractParent
的新 Foo
和 class Bar extends AbstractParent
的新 Bar
?
一种可维护的方法是将 times
计算保留在基础 class 中,并在 Foo
和 Bar
中实现单独的 create
工厂方法。这样,基础 class 就不知道其扩展子 classes.
您在 AbstractParent
中将 create
声明为 abstract
以确保它将在所有子 class 中实现。 polymorphic this return type ensures for the caller of times
, that the returned instance has the same sub class type (Playground).
abstract class AbstractParent {
protected value: number;
constructor(value: number) {
this.value = value;
}
times(multiplicator: number) {
return this.create(this.value * multiplicator);
}
// factory method creates instances of same polymorphic type (Foo / Bar)
protected abstract create(value: number): this
}
class Foo extends AbstractParent {
create(value: number) {
// Polymorphic this type is Foo here, so it's OK to return a Foo.
// Compiler is not aware of the class context, so we cast.
return new Foo(value) as this
}
}
class Bar extends AbstractParent {
// same as Foo
create(value: number) {
return new Bar(value) as this
}
}
const foo = new Foo(42).times(2) // Foo
const bar = new Bar(42).times(3) // Bar
您可以将 child 类型传递给 parent 构造函数并保存引用。这样可以避免 parent 必须了解所有可能的 child 类型(尽管我同意另一个答案,即可能有更好的方法来实现您的总体目标)。像这样 (playground link):
type ChildConstructor<Child> = new (value: number, childConstructor: ChildConstructor<Child>) => Child;
abstract class AbstractParent<Child extends AbstractParent<Child>> {
protected value: number;
protected childConstructor: ChildConstructor<Child>;
constructor(value: number, childConstructor: ChildConstructor<Child>) {
this.value = value;
this.childConstructor = childConstructor;
}
public times(multiplicator: number): Child {
return new this.childConstructor(this.value * multiplicator, this.childConstructor);
}
}
class Foo extends AbstractParent<Foo> {
constructor(value: number) {
super(value, Foo);
}
}
class Bar extends AbstractParent<Bar> {
constructor(value: number) {
super(value, Bar);
}
}
请注意,要获得 100% 的类型安全性,每个 child class 都需要至少一个额外的私有 属性 或方法来防止 TypeScript 的 "duck typing"考虑到 classes 是可以互换的(因为它们共享相同的属性和方法)。
是否可以有这样的伪代码:
abstract class AbstractParent {
protected value: number;
constructor(value: number) {
this.value = value;
}
...
public times(multiplicator: number) {
return new ???(value * multiplicator);
}
}
returns class Foo extends AbstractParent
的新 Foo
和 class Bar extends AbstractParent
的新 Bar
?
一种可维护的方法是将 times
计算保留在基础 class 中,并在 Foo
和 Bar
中实现单独的 create
工厂方法。这样,基础 class 就不知道其扩展子 classes.
您在 AbstractParent
中将 create
声明为 abstract
以确保它将在所有子 class 中实现。 polymorphic this return type ensures for the caller of times
, that the returned instance has the same sub class type (Playground).
abstract class AbstractParent {
protected value: number;
constructor(value: number) {
this.value = value;
}
times(multiplicator: number) {
return this.create(this.value * multiplicator);
}
// factory method creates instances of same polymorphic type (Foo / Bar)
protected abstract create(value: number): this
}
class Foo extends AbstractParent {
create(value: number) {
// Polymorphic this type is Foo here, so it's OK to return a Foo.
// Compiler is not aware of the class context, so we cast.
return new Foo(value) as this
}
}
class Bar extends AbstractParent {
// same as Foo
create(value: number) {
return new Bar(value) as this
}
}
const foo = new Foo(42).times(2) // Foo
const bar = new Bar(42).times(3) // Bar
您可以将 child 类型传递给 parent 构造函数并保存引用。这样可以避免 parent 必须了解所有可能的 child 类型(尽管我同意另一个答案,即可能有更好的方法来实现您的总体目标)。像这样 (playground link):
type ChildConstructor<Child> = new (value: number, childConstructor: ChildConstructor<Child>) => Child;
abstract class AbstractParent<Child extends AbstractParent<Child>> {
protected value: number;
protected childConstructor: ChildConstructor<Child>;
constructor(value: number, childConstructor: ChildConstructor<Child>) {
this.value = value;
this.childConstructor = childConstructor;
}
public times(multiplicator: number): Child {
return new this.childConstructor(this.value * multiplicator, this.childConstructor);
}
}
class Foo extends AbstractParent<Foo> {
constructor(value: number) {
super(value, Foo);
}
}
class Bar extends AbstractParent<Bar> {
constructor(value: number) {
super(value, Bar);
}
}
请注意,要获得 100% 的类型安全性,每个 child class 都需要至少一个额外的私有 属性 或方法来防止 TypeScript 的 "duck typing"考虑到 classes 是可以互换的(因为它们共享相同的属性和方法)。