You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为TypeScript类注册表及动态配置添加类型定义?

问题描述

需要根据JSON/YAML格式的配置动态创建对象,配置的键是类名字符串,值是对应类的构造函数参数。现有多个继承自BaseClass的子类,存入注册表后,处理配置时要完成:

  • 根据类名从注册表查找对应类
  • 传入参数创建实例
  • 调用doSomething方法

当前代码编译失败,核心需解决两个问题:

  1. 如何给注册表变量添加正确类型?
  2. 如何给配置添加类型,确保构造函数能正常调用?

原编译失败代码:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 10:55:20