TypeScript 工厂方法场景下如何正确声明不同产品的对应类型?
错误根源
你定义的抽象父类Product将配置属性config声明为ConfigI联合类型,TypeScript 对联合类型的属性访问默认只允许访问所有子类型共有的属性,而你的ConfigProduct1和ConfigProduct2没有公共属性,因此不管是在子类中访问独有属性arg1/arg2,还是外部通过Product类型的实例访问配置属性,都会触发类型报错。另外你的工厂方法没有做类型映射,TypeScript 无法根据传入的TYPE枚举值匹配对应的产品类型和配置类型。
修复方案
1. 将抽象父类改造为泛型类
给Product增加泛型参数,约束泛型必须继承自ConfigI,让子类继承时可以指定具体的配置类型:
// 父类增加泛型参数 abstract class Product<T extends ConfigI> { protected config: T get cfg(): T { return this.config } constructor(config: T) { this.config = config } abstract foo(): void }
2. 子类继承时指定具体配置类型
子类继承Product时传入对应产品的配置类型,TypeScript 就能识别子类中config的具体类型:
class ConcreteProduct1 extends Product<ConfigProduct1> { foo() { console.log(this.config.arg1) // 无报错 } } class ConcreteProduct2 extends Product<ConfigProduct2> { foo() { console.log(this.config.arg2) // 无报错 } }
3. 给工厂方法增加重载做类型映射
通过函数重载让 TypeScript 可以根据传入的产品类型,自动匹配对应的配置类型和返回值类型:
class Creator { // 重载1:产品1类型对应产品1配置和产品1实例 static createProduct(type: TYPE.PRODUCT1, config: ConfigProduct1): ConcreteProduct1 // 重载2:产品2类型对应产品2配置和产品2实例 static createProduct(type: TYPE.PRODUCT2, config: ConfigProduct2): ConcreteProduct2 // 实现签名 static createProduct(type: TYPE, config: ConfigI) { switch(type) { case TYPE.PRODUCT1: return new ConcreteProduct1(config) case TYPE.PRODUCT2: return new ConcreteProduct2(config) default: throw new Error('Wrong Product type') } } }
此时调用工厂方法时,TypeScript 会自动做类型校验:传入TYPE.PRODUCT1时必须传ConfigProduct1类型的配置,返回值也会自动推断为ConcreteProduct1类型。
4. 外部使用产品实例时的类型处理
如果需要在其他类中访问产品的具体配置属性,有两种可选方案:
- 方案1:给使用类也增加泛型约束
class ProductUsing<T extends Product<ConfigI>> { protected product: T constructor(product: T) { this.product = product } bar() { // 当传入的product是ConcreteProduct1实例时,访问arg1无报错 console.log(this.product.cfg.arg1) } }
- 方案2:先做类型收窄再访问属性
如果无法确定传入的产品类型,可以先通过属性判断收窄配置的联合类型:
bar() { const cfg = this.product.cfg if ('arg1' in cfg) { console.log(cfg.arg1) // 收窄为ConfigProduct1类型,无报错 } else { console.log(cfg.arg2) // 收窄为ConfigProduct2类型,无报错 } }
内容的提问来源于stack exchange,提问作者yiooxir
相关产品推荐
相关产品推荐

