我有一个抽象基本类型和它的一堆子类型。有些子类型本身是抽象的,并且还有更多的子类型。
我希望能够定义多态函数,其中一些可能是抽象的,而另一些则具有可能被某些节点重载的默认实现。
在 TypeScript 中对此进行建模的最佳方法是什么?
在 JavaScript 中我会使用
class
es。这是一个愚蠢而简单的例子,它展示了我要写的内容。请不要担心语义,只需担心我正在尝试建模的数据类型。
class Polygon {
constructor( name ) { this.name = name; }
// abstract methods
area() { assert.fail(`${this.constructor.name} must implement area()`); }
// methods with fallback implementation
toString() {
const props = Object.entries(this)
.filter( ([k, v])=>k !== 'name' )
.map( ([k, v])=>`${k}:${v}` )
.join(", ");
return `${this.name}{props}`;
}
}
class Triangle extends Shape {
constructor( base, height ) {
super('TRIANGLE');
this.base = base;
this.height = height;
}
area() { return this.base*this.height/2; }
}
class Quadrilateral extends Shape {
constructor( name, base, height ) {
super(name);
this.base = base;
this.height = height;
}
area() { return this.base*this.height; }
}
class Square extends Quadrilateral {
constructor( side ) {
super( 'SQUARE', side, side );
}
area() { return this.base*this.height; }
toString() { return `${this.name}{side:${this.base}}`; }
}
然而,以简单的方式在 TypeScript 中转换这个东西效果不太好:
字段
name
不能用作Polygon
的判别式。如果我想使用正确的判别式,我必须引入: type PolygonType = Triangle | Square;
并使用 PolygonType
而不是 Polygon
作为其未知子类型的类型。
使用
class
限制了我的表达能力。例如,我无法使用类型级元编程来帮助我定义类型的确切形状。
更适合 TypeScript 的实现是使用
type
对我的类型进行建模。但这意味着放弃通过原型继承实现的多态性,以及 JavaScript class
es 的所有其他好处。
我想知道是否有其他解决方案来解决类似的问题,我预计这将是非常常见的,以及是否有任何解决方案是普遍首选的。
在这种情况下,使用 interfaces 而不是类型似乎是有益的。
首先,接口指定了类型,这样目的就达到了。
另一方面,接口从字面上和历史上看都是一个抽象类,没有单个实现的方法。
因此,如果它是一个类,则可以像类一样进行扩展以实现多态目的:
interface Shape {
area(): number
}
interface Polygon extends Shape {
toString(): string
}
class Triangle implements Shape {}
class Square implements Polygon {}