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

TypeScript将泛型对象数组转为对象集合时如何正确保留类型

解决方案

调整说明

需要同时修改库侧代码,配合少量应用层调整即可满足需求,核心问题在于原泛型设计没有建立配置name字面量和对应Entity类型的映射关系,且没有收窄配置数组的字面量类型。

库代码修改

基础类型调整

// 为WrapperConfig增加Name泛型参数存储名字面量类型
export type WrapperConfig<Name extends string, Entity> = {
    name: Name
}

type Wrapper<Entity> = {
    name: string,
    data: Entity
    // .. 其他原有属性
}

createWrapper 调整

function createWrapper<Name extends string, Entity>(wrapperConfig: WrapperConfig<Name, Entity>): Wrapper<Entity>  {
    // 原有实现逻辑不变
    return { name: wrapperConfig.name, data: {} as Entity } as Wrapper<Entity>
}

createManyWrappers 重写

// 使用const泛型自动收窄传入配置数组的字面量类型,遍历映射生成返回值类型
export function createManyWrappers<const T extends WrapperConfig<string, any>[]>(wrapperConfigs: T) {
    const wrappers = {} as {
        // 键重映射生成带Wrapper后缀的属性名,同时提取对应配置的Entity类型
        [K in T[number] as `${K['name']}Wrapper`]: Wrapper<K extends WrapperConfig<any, infer E> ? E : never>
    };

    for (const wrapperConfig of wrapperConfigs) {
        const nameWithSuffix = `${wrapperConfig.name}Wrapper` as keyof typeof wrappers;
        wrappers[nameWithSuffix] = createWrapper(wrapperConfig);
    }

    return wrappers;
}

如果需要兼容TS4.5以下版本,去掉泛型前的const修饰,在应用层传入配置时加as const即可。

应用层调整

无需大量修改,只需保证配置的name类型为字面量即可,两种常用写法:

写法1:直接传入配置字面量(改动最小)

type Entity1 = { foo: string };
type Entity2 = { bar: number };
type Entity3 = { cor: number };

const wrappers = createManyWrappers([
    { name: 'entity1' } as WrapperConfig<'entity1', Entity1>,
    { name: 'entity2' } as WrapperConfig<'entity2', Entity2>,
    { name: 'entity3' } as WrapperConfig<'entity3', Entity3>
]);

// 现在属性可自动补全提示
const { entity1Wrapper, entity2Wrapper, entity3Wrapper } = wrappers;

const data1 = entity1Wrapper.data // 类型为 Entity1
const data2 = entity2Wrapper.data // 类型为 Entity2
const data3 = entity3Wrapper.data // 类型为 Entity3

写法2:提前定义配置数组(加as const收窄类型)

const wrapperConfigs = [
    { name: 'entity1' } as WrapperConfig<'entity1', Entity1>,
    { name: 'entity2' } as WrapperConfig<'entity2', Entity2>,
    { name: 'entity3' } as WrapperConfig<'entity3', Entity3>
] as const; // 必须加as const收窄为tuple类型,否则name会被拓宽为string

const wrappers = createManyWrappers(wrapperConfigs);

可选简化写法(库侧可加工具函数)

如果要简化应用层类型声明,可在库侧增加配置定义工具函数:

// 库侧新增
export function defineConfig<Name extends string, Entity>(name: Name, entitySample?: Entity): WrapperConfig<Name, Entity> {
    return { name } as WrapperConfig<Name, Entity>;
}

应用层使用更简洁:

const wrappers = createManyWrappers([
    defineConfig('entity1', {} as Entity1),
    defineConfig('entity2', {} as Entity2),
    defineConfig('entity3', {} as Entity3)
]);

内容的提问来源于stack exchange,提问作者mdrobny

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 20:45:02