如何为TypeScript类注册表及动态配置添加类型定义?
问题描述
需要根据JSON/YAML格式的配置动态创建对象,配置的键是类名字符串,值是对应类的构造函数参数。现有多个继承自BaseClass的子类,存入注册表后,处理配置时要完成:
- 根据类名从注册表查找对应类
- 传入参数创建实例
- 调用
doSomething方法
当前代码编译失败,核心需解决两个问题:
- 如何给注册表变量添加正确类型?
- 如何给配置添加类型,确保构造函数能正常调用?
原编译失败代码:
abstract class BaseClass { abstract doSomething(): void } interface ComplexSomethingParam { key: string key2: number } interface SomethingParams { param1: string param2: number param3: ComplexSomethingParam } class Something extends BaseClass { readonly param1: string readonly param2: number readonly param3: ComplexSomethingParam constructor({ param1, param2, param3 }: SomethingParams) { super() this.param1 = param1 this.param2 = param2 this.param3 = param3 } doSomething(): void { console.log("I am something !") } } interface ComplexAnotherParam { anotherKey: string anotherKey2: number } interface AnotherParams { anotherParam1: string anotherParam2: number anotherParam3: ComplexAnotherParam } class AnotherThing extends BaseClass { readonly anotherParam1: string readonly anotherParam2: number readonly anotherParam3: ComplexAnotherParam constructor({ anotherParam1, anotherParam2, anotherParam3 }: AnotherParams) { super() this.anotherParam1 = anotherParam1 this.anotherParam2 = anotherParam2 this.anotherParam3 = anotherParam3 } doSomething(): void { console.log("I am another thing !") } } // 类型错误:注册表存的是构造函数,不是实例 const REGISTRY: Record<string, BaseClass> = { Something, AnotherThing } // 无类型约束,参数可能不匹配 const CONFIG = { "Something": { "param1": "value", "param2": 1234, "param3": { "key1": "value", // 键名错误,应为key "key2": 456 } }, "AnotherThing": { "anotherParam1": "value", "anotherParam2": 1234, "anotherParam3": { "anotherKey1": "value", // 键名错误,应为anotherKey "anotherKey2": 456 } } } // 参数无类型,循环逻辑错误 function handleConfig(config) { for (const className of config) { const klass = REGISTRY[className] if (klass === undefined) { console.error(`Unknown permission class ${className}`) return } const instance = new klass(config[className]) instance.doSomething() } } handleConfig(CONFIG)
解决方案
1. 注册表类型修正
注册表存储的是类的构造函数,而非实例,需要定义构造函数类型:
// 定义接收单个对象参数、返回BaseClass实例的构造函数类型 type BaseConstructor<T extends BaseClass = BaseClass> = new (params: any) => T; // 注册表类型改为Record<string, BaseConstructor> const REGISTRY: Record<string, BaseConstructor> = { Something, AnotherThing };
2. 配置类型强约束
通过映射类型建立类名与参数类型的强关联,确保配置键值对应:
// 类名与对应参数类型的映射 type ClassParamsMap = { Something: SomethingParams; AnotherThing: AnotherParams; }; // 配置类型严格匹配映射关系,同时修正原代码中的键名错误 const CONFIG: { [K in keyof ClassParamsMap]: ClassParamsMap[K] } = { "Something": { "param1": "value", "param2": 1234, "param3": { "key": "value", "key2": 456 } }, "AnotherThing": { "anotherParam1": "value", "anotherParam2": 1234, "anotherParam3": { "anotherKey": "value", "anotherKey2": 456 } } };
3. 修正handleConfig函数
修复循环逻辑,添加类型安全校验:
function handleConfig(config: typeof CONFIG) { // 遍历配置键并断言为ClassParamsMap的键类型,保证类型匹配 for (const className of Object.keys(config) as Array<keyof ClassParamsMap>) { const klass = REGISTRY[className]; if (!klass) { console.error(`Unknown class ${className}`); continue; // 用continue避免单个错误终止所有处理 } const instance = new klass(config[className]); instance.doSomething(); } }
完整可编译代码
abstract class BaseClass { abstract doSomething(): void; } interface ComplexSomethingParam { key: string; key2: number; } interface SomethingParams { param1: string; param2: number; param3: ComplexSomethingParam; } class Something extends BaseClass { readonly param1: string; readonly param2: number; readonly param3: ComplexSomethingParam; constructor({ param1, param2, param3 }: SomethingParams) { super(); this.param1 = param1; this.param2 = param2; this.param3 = param3; } doSomething(): void { console.log("I am something !"); } } interface ComplexAnotherParam { anotherKey: string; anotherKey2: number; } interface AnotherParams { anotherParam1: string; anotherParam2: number; anotherParam3: ComplexAnotherParam; } class AnotherThing extends BaseClass { readonly anotherParam1: string; readonly anotherParam2: number; readonly anotherParam3: ComplexAnotherParam; constructor({ anotherParam1, anotherParam2, anotherParam3 }: AnotherParams) { super(); this.anotherParam1 = anotherParam1; this.anotherParam2 = anotherParam2; this.anotherParam3 = anotherParam3; } doSomething(): void { console.log("I am another thing !"); } } type BaseConstructor<T extends BaseClass = BaseClass> = new (params: any) => T; const REGISTRY: Record<string, BaseConstructor> = { Something, AnotherThing }; type ClassParamsMap = { Something: SomethingParams; AnotherThing: AnotherParams; }; const CONFIG: { [K in keyof ClassParamsMap]: ClassParamsMap[K] } = { "Something": { "param1": "value", "param2": 1234, "param3": { "key": "value", "key2": 456 } }, "AnotherThing": { "anotherParam1": "value", "anotherParam2": 1234, "anotherParam3": { "anotherKey": "value", "anotherKey2": 456 } } }; function handleConfig(config: typeof CONFIG) { for (const className of Object.keys(config) as Array<keyof ClassParamsMap>) { const klass = REGISTRY[className]; if (!klass) { console.error(`Unknown class ${className}`); continue; } const instance = new klass(config[className]); instance.doSomething(); } } handleConfig(CONFIG);
内容的提问来源于stack exchange,提问作者KiriFafe
相关产品推荐
相关产品推荐

