如何结合工厂模式与泛型基类保障TypeScript类型安全?
泛型基类工厂模式的TypeScript类型推断问题修复
问题场景
使用TypeScript实现结合泛型基类的工厂模式时,动态创建类实例后调用方法出现类型推断错误。简化代码如下:
abstract class BaseClass<TArg> { async getArgsArray(): Promise<TArg[]> { return await fetch("...").then(resp => resp.json()) as TArg[] } } type Class1Arg = { myValue: string } class Class1 extends BaseClass<Class1Arg> { methodWithArgs(args: Class1Arg[]) { return args[0]?.myValue } } type Class2Arg = { myOtherValue: string } class Class2 extends BaseClass<Class2Arg> { methodWithArgs(args: Class2Arg[]) { return args[0]?.myOtherValue } } async function getFactoryKey(): Promise<"class1" | "class2"> { return await fetch("...").then(resp => resp.json()) as Promise<"class1" | "class2"> } const classes = { class1: Class1, class2: Class2 } function factory<K extends keyof typeof classes>( key: K ): InstanceType<(typeof classes)[K]> { const TransformerClass = classes[key] return new TransformerClass() as InstanceType<(typeof classes)[K]> } async function test() { const key = await getFactoryKey() const myClass = factory(key) const args = await myClass.getArgsArray() // 此处触发TypeScript错误 myClass.methodWithArgs(args) }
错误信息
Argument of type 'Promise<Class2Arg[]> | Promise<Class1Arg[]>' is not assignable to parameter of type 'Class2Arg & Class1Arg'. Type 'Promise<Class2Arg[]>' is not assignable to type 'Class2Arg & Class1Arg'. Property 'myOtherValue' is missing in type 'Promise<Class2Arg[]>' but required in type 'Class2Arg'.
问题根源
TypeScript无法对联合类型实例进行关联类型推断:当myClass被推断为Class1 | Class2联合类型时,args会被推断为Class1Arg[] | Class2Arg[],但TypeScript无法将两者的类型一一绑定,导致调用methodWithArgs时认为参数类型不匹配。
修复方案
以下方案均能保留工厂模式的灵活性,同时保障类型安全:
方案一:类型守卫缩小范围(简单直接)
通过判断key的值,明确当前实例的具体类型,让TypeScript关联对应参数类型:
async function test() { const key = await getFactoryKey() const myClass = factory(key) const args = await myClass.getArgsArray() if (key === "class1") { (myClass as Class1).methodWithArgs(args as Class1Arg[]) } else { (myClass as Class2).methodWithArgs(args as Class2Arg[]) } }
方案二:泛型函数封装关联逻辑(优雅安全)
将实例创建、参数获取与方法调用封装到单一泛型函数中,让TypeScript在固定泛型上下文里完成类型推断:
// 封装关联逻辑的泛型函数 async function handleInstance<K extends keyof typeof classes>(key: K) { const myClass = factory(key) const args = await myClass.getArgsArray() myClass.methodWithArgs(args) // 类型完全匹配,无报错 } async function test() { const key = await getFactoryKey() await handleInstance(key) }
此方案的核心是:泛型K在handleInstance中被固定为单一键类型(而非联合类型),TypeScript能准确关联myClass与args的对应类型。
方案三:优化工厂函数类型定义(进阶)
通过定义关联泛型参数的构造函数类型,强化工厂函数的类型约束:
// 定义构造函数类型,关联泛型参数与方法类型 type BaseClassConstructor<TArg> = new () => BaseClass<TArg> & { methodWithArgs(args: TArg[]): unknown } // 明确classes的类型 const classes: Record<string, BaseClassConstructor<any>> = { class1: Class1, class2: Class2 } // 工厂函数类型保持不变 function factory<K extends keyof typeof classes>( key: K ): InstanceType<typeof classes[K]> { const TransformerClass = classes[key] return new TransformerClass() as InstanceType<typeof classes[K]> } // 配合方案二的封装函数使用,类型安全更完善
内容的提问来源于stack exchange,提问作者WanderingCoder
相关产品推荐
相关产品推荐

