TypeScript有界多态方案中泛型T的非any默认值设置方法
二叉搜索树(BST)与统计二叉搜索树的三种TypeScript实现方案
我尝试为二叉搜索树(BST)以及带有额外size属性的统计二叉搜索树建模,试验了三种实现方案:
- 递归方案
- 多态方案
- 有界多态方案
1. 递归方案
递归方案对子类支持不佳,每个基类方法都需要在子类中重写以匹配子类类型。
// 1. 递归方案 interface IBSTRec<K, V> { key: K; value: V; left?: IBSTRec<K, V>; right?: IBSTRec<K, V>; } class BSTRec<K, V> implements IBSTRec<K, V> { constructor( public key: K, public value: V, public left?: BSTRec<K, V>, public right?: BSTRec<K, V> ) { /* ... */ } insert(node: BSTRec<K, V>): BSTRec<K, V> { // Insert logic... return node; } } interface IStatisticBSTRec<K, V> extends BSTRec<K, V> { size: number; } class StatisticBSTRec<K, V> extends BSTRec<K, V> { size = 1; constructor(key: K, value: V, left?: StatisticBSTRec<K, V>, right?: StatisticBSTRec<K, V>) { super(key, value, left, right); this.size += (left?.size || 0) + (right?.size || 0); } insert(node: StatisticBSTRec<K, V>): StatisticBSTRec<K, V> { const inserted = super.insert(node); // Update size for inserted node's ancestors... return inserted as StatisticBSTRec<K, V>; } }
2. 多态方案
多态方案表现良好,子类无需进行类型断言即可适配基类方法。
// 2. 多态方案 interface IBSTPoly<K, V> { key: K; value: V; left?: this; right?: this; } class BSTPoly<K, V> implements IBSTPoly<K, V> { key: K; value: V; left?: this; right?: this; constructor(key: K, value: V, left?: BSTPoly<K, V>, right?: BSTPoly<K, V>) { this.key = key; this.value = value; if (left) this.left = left as this; if (right) this.right = right as this; } insert(node: this): this { // Insert logic... return this; } } interface IStatisticBSTPoly<K, V> extends IBSTPoly<K, V> { size: number; } class StatisticBSTPoly<K, V> extends BSTPoly<K, V> implements IStatisticBSTPoly<K, V> { size = 1; constructor(key: K, value: V, left?: StatisticBSTPoly<K, V>, right?: StatisticBSTPoly<K, V>) { super(key, value, left, right); this.size += (left?.size || 0) + (right?.size || 0); } insert(node: this): this { const inserted = super.insert(node); // Update size for inserted node's ancestors... return inserted; } }
3. 有界多态方案(待解决问题)
在探索用TypeScript泛型约束描述节点递归关系的有界多态方案时,遇到无法将泛型参数T自身作为默认值的问题,当前只能用any填充默认值。需要解决:如何在不使用any的情况下为T设置正确的默认值?
// 3. 有界多态方案 interface IBSTBoRec<K, V, T extends IBSTBoRec<K, V, T> = IBSTBoRec<K, V, any>> { key: K; value: V; left?: T; right?: T; } // Can't use T for its own default, so using any. class BSTBoRec<K, V, T extends IBSTBoRec<K, V, T> = BSTBoRec<K, V, any>> implements IBSTBoRec<K, V, T> { constructor( public key: K, public value: V, public left?: T, public right?: T ) { /* ... */ } insert(node: T): T { // Insert logic... return node; } } interface IStatisticBSTBoRec<K, V, T extends IStatisticBSTBoRec<K, V, T> = IStatisticBSTBoRec<K, V, any>> extends IBSTBoRec<K, V, T> { size: number; } class StatisticBSTBoRec<K, V, T extends StatisticBSTBoRec<K, V, T> = StatisticBSTBoRec<K, V, any>> extends BSTBoRec<K, V, T> implements IStatisticBSTBoRec<K, V, T> { size = 1; constructor(key: K, value: V, left?: T, right?: T) { super(key, value, left, right); this.size += (left?.size || 0) + (right?.size || 0); } insert(node: T): T { const inserted = super.insert(node); // Update size for inserted node's ancestors... return inserted; } }
解决方案:使用自引用默认类型
TypeScript不允许直接将泛型参数自身作为默认值,但可以通过定义自引用的基础类型替代any,保证类型安全。
步骤1:定义默认自引用BST节点类型
// 定义默认的自引用BST节点类型 class DefaultBSTBoRec<K, V> implements IBSTBoRec<K, V, DefaultBSTBoRec<K, V>> { constructor( public key: K, public value: V, public left?: DefaultBSTBoRec<K, V>, public right?: DefaultBSTBoRec<K, V> ) {} insert(node: DefaultBSTBoRec<K, V>): DefaultBSTBoRec<K, V> { // 实现默认插入逻辑 return node; } }
步骤2:修改泛型默认值
将接口和类的泛型默认值从any替换为上述自引用类型:
interface IBSTBoRec<K, V, T extends IBSTBoRec<K, V, T> = DefaultBSTBoRec<K, V>> { key: K; value: V; left?: T; right?: T; insert(node: T): T; } class BSTBoRec<K, V, T extends IBSTBoRec<K, V, T> = DefaultBSTBoRec<K, V>> implements IBSTBoRec<K, V, T> { constructor( public key: K, public value: V, public left?: T, public right?: T ) { /* ... */ } insert(node: T): T { // Insert logic... return node; } }
步骤3:为统计二叉搜索树适配默认类型
// 定义默认的自引用统计BST节点类型 class DefaultStatisticBSTBoRec<K, V> extends BSTBoRec<K, V, DefaultStatisticBSTBoRec<K, V>> implements IStatisticBSTBoRec<K, V, DefaultStatisticBSTBoRec<K, V>> { size = 1; constructor(key: K, value: V, left?: DefaultStatisticBSTBoRec<K, V>, right?: DefaultStatisticBSTBoRec<K, V>) { super(key, value, left, right); this.size += (left?.size || 0) + (right?.size || 0); } insert(node: DefaultStatisticBSTBoRec<K, V>): DefaultStatisticBSTBoRec<K, V> { const inserted = super.insert(node); // 更新size逻辑 return inserted; } } // 修改统计BST的泛型默认值 interface IStatisticBSTBoRec<K, V, T extends IStatisticBSTBoRec<K, V, T> = DefaultStatisticBSTBoRec<K, V>> extends IBSTBoRec<K, V, T> { size: number; } class StatisticBSTBoRec<K, V, T extends StatisticBSTBoRec<K, V, T> = DefaultStatisticBSTBoRec<K, V>> extends BSTBoRec<K, V, T> implements IStatisticBSTBoRec<K, V, T> { size = 1; constructor(key: K, value: V, left?: T, right?: T) { super(key, value, left, right); this.size += (left?.size || 0) + (right?.size || 0); } insert(node: T): T { const inserted = super.insert(node); // Update size for inserted node's ancestors... return inserted; } }
核心思路
通过定义具体的自引用默认类型,既满足泛型约束的递归要求,又避免any带来的类型不安全问题。当未显式指定泛型参数T时,自动使用该默认类型,保证类型检查的严谨性。
内容的提问来源于stack exchange,提问作者Robert Kajic
相关产品推荐
相关产品推荐

