TypeScript - Copy-Constructor(不允许多个构造函数实现)
TypeScript - Copy-Constructor(不允许多个构造函数实现)
TypeScript是否支持复制构造函数(例如C++的示例)?
当答案是否定的(或者说尚未支持)时,那么初始化我们扩展的基类并从现有实例(具有相同基类类型)复制的最佳做法是什么?
我尝试了但是出现了错误:
不允许有多个构造函数实现
当前代码:
目前我们的代码使用了手动声明的基类的copy()方法,该方法需要基类已经初始化,
但是我们的基类(ShopConfig)在其构造函数中有一些相当昂贵的操作,这些操作已经完成了一次,如果TypeScript中有复制构造函数的概念实现,那么这些操作将不再需要。
class ShopConfig {
public apiKey: string;
public products: any;
constructor(apiKey: string = 'trial') {
this.apiKey = apiKey;
//从本地数据库获取产品列表
this.products = expensiveDataBaseQuery();
}
protected copy(other: ShopConfig) {
for (const field in other) {
if (other.hasOwnProperty(field)) {
this[field] = other[field];
}
}
}
}
class ShopManager extends ShopConfig {
constructor(config: ShopConfig) {
super();
super.copy(config);
console.log('ShopManager configurations:', config);
}
}
在TypeScript中,没有直接的拷贝构造函数的功能。但是可以通过模拟实现来达到类似的效果。下面给出了一个示例代码:
class A { public propertyA: string; public propertyB: string; private copy(object: A) { object.propertyA = this.propertyA; object.propertyB = this.propertyB; } public clone() { const obj = new A(); this.copy(obj); return obj; } }
上述代码实现了一个类A,其中包含了一个私有方法`copy`用于复制对象的属性,以及一个公共方法`clone`用于创建并返回一个新的A类实例。
如果构造函数中包含了复杂的逻辑,可以将其移动到另一个函数中,比如命名为`initialize`,然后在构造函数之外调用该函数。可以使用工厂方法模式来避免在创建对象时需要手动调用`initialize`方法,或者可以通过构造函数参数的方式,在`clone`方法内部创建新对象时不调用`initialize`方法。
如果需要频繁进行对象的复制操作(比如实现原型设计模式),建议使用lodash库中的`cloneDeep`方法。
然而,如果需要构造基类或父类的拷贝,上述方法并不能满足要求。将耗时的逻辑移到其他方法中并手动调用会增加出错的可能性,因此不能完全替代基于继承的拷贝构造函数的功能。
总之,TypeScript中没有直接支持拷贝构造函数的功能。在构造函数中应该尽量保持简单,只进行初始属性的赋值。如果需要避免在构造函数中执行耗时的逻辑,可以将其分离到其他对象中,或者使用其他设计模式来解决问题。
需要注意的是,TypeScript允许构造函数和方法接受多个类型的参数,这是语言的基本特性。如果认为这种复杂性违反了某些模式,可以选择不使用TypeScript,或者至少接受它作为语言的一部分(而不是其他开发者的错误)。在整个项目中都需要手动调用分离方法来构造对象,会导致代码更加复杂和难以维护。
如果你认为上述方法过于复杂,你应该知道有些项目的方法可能有1000多行代码,并且我发明了折叠滚动条(github.com/microsoft/vscode/issues/72807),因为在这个行业中,我们更多地处理的是别人的代码,而不是自己的代码。
在TypeScript中,不允许有多个构造函数的实现。然而,我们可以通过一些方法来实现类似于复制构造函数的功能。
首先,我们需要修改类的构造函数参数类型,使其同时接受构造函数和复制构造函数的类型。我们可以使用类型的组合来实现,使用`|`操作符连接不同的类型。
然后,在构造函数的实现中,我们需要检查参数的类型。对于类类型的参数,我们可以使用`instanceof`操作符进行检查;对于基本类型的参数,我们可以使用`typeof`操作符进行检查。
最后,根据参数的类型来决定是进行构造还是复制操作,并处理相关的任务(例如,如果是复制构造函数,则进行复制操作)。
以上是实现复制构造函数的一种方法。下面是一个示例代码:
class ShopConfig { public apiKey: string; public products: any; constructor( v: ShopConfig | string | String | null = 'trial' ) { if ( ! v) { throw new Error('ShopConfig: expected API-Key or existing instance'); } else if (v instanceof ShopConfig) { for (const field in v) { if (v.hasOwnProperty(field)) { this[field] = v[field]; } } } else if (typeof v === 'string' || v instanceof String) { this.apiKey = v.toString(); this.products = expensiveDataBaseQuery(); } } } class ShopManager extends ShopConfig { constructor(config: ShopConfig) { super(config); console.log('ShopManager configurations:', config); } }
需要注意的是,上述复制相关的逻辑并不复杂,只有几行代码。如果逻辑更复杂,我们可以在基类中创建一个`_copy(...)`方法(可能使用`protected`修饰符而不是`private`,以便可以被子类覆盖),然后在同一个类的构造函数中调用该方法(在类型检查之后)。
另外,可能需要将`ShopConfig`放在构造函数的类型链的最后,以确保正常构造函数的优先级高于复制构造函数,但我们将其放在第一位是为了确保用户注意到可以进行复制操作。