TypeScript进阶泛型:createMigrator函数类型安全实现问题
类型安全数据迁移函数实现思路
核心问题分析
报错的根本原因是:你传入的versions是as const声明的只读元组,但原类型定义里的VersionInit<T>返回的是可变数组类型,TypeScript不允许将只读类型赋值给可变类型。同时原泛型逻辑的元组递归推断也存在细节问题,导致类型匹配失败。
分步解决思路
1. 适配只读元组类型
将所有涉及版本列表的类型定义改为支持只读元组,把VersionInit和VersionNext的返回类型从普通数组改为只读数组:
interface BaseVersion<T> { schema: T; } interface VersionWithUpgrade<T, PrevSchema> extends BaseVersion<T> { upgrade: (prev: PrevSchema) => T; // 修正:upgrade应返回当前版本的schema数据,而非void } // 处理第一个版本(无upgrade) type VersionInit<T extends readonly any[]> = T extends readonly [infer FirstSchema, ...infer Rest] ? readonly [BaseVersion<FirstSchema>, ...VersionNext<FirstSchema, Rest>] : readonly []; // 递归处理后续版本(每个版本的upgrade接收前一个版本的schema) type VersionNext<PrevSchema, T extends readonly any[]> = T extends readonly [infer CurrentSchema, ...infer Rest] ? readonly [VersionWithUpgrade<CurrentSchema, PrevSchema>, ...VersionNext<CurrentSchema, Rest>] : readonly [];
2. 调整泛型推断逻辑
让createMigrator直接推断versions的元组类型,避免间接泛型导致的推断失效:
interface MigratorOptions<Versions extends readonly any[]> { versions: VersionInit<Versions>; } function createMigrator<Versions extends readonly any[]>( options: MigratorOptions<Versions> ) { // 实现迁移逻辑:根据当前数据版本,依次调用对应upgrade函数 return { migrate: <V extends Versions[number]['schema']>(currentData: V, targetVersion: number) => { let data: any = currentData; const currentVersionIndex = options.versions.findIndex(v => v.schema === data); for (let i = currentVersionIndex + 1; i <= targetVersion; i++) { const upgradeFn = options.versions[i].upgrade; if (upgradeFn) { data = upgradeFn(data); } } return data; } }; }
3. 验证类型安全
现在使用你提供的示例调用代码,TypeScript会自动推断出upgrade函数的prev参数类型为schemaV1的类型,同时确保每个版本的upgrade只能接收前一个版本的schema数据:
const schemaV1 = { users: {}, } as const; const schemaV2 = { users: {}, comments: {}, } as const; const migrator = createMigrator({ versions: [ { schema: schemaV1 }, { schema: schemaV2, upgrade: (prev) => { // prev自动推断为typeof schemaV1 return { ...prev, comments: [] }; } }, ] as const, });
关键优化点
- 将所有版本列表类型改为
readonly,匹配as const的传入类型 - 修正
upgrade函数的返回值类型:确保返回当前版本的schema数据,保证数据流转的类型安全 - 递归类型
VersionNext正确传递前一个版本的schema类型,实现版本间的类型链约束
内容的提问来源于stack exchange,提问作者lenny.myr
相关产品推荐
相关产品推荐

