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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 08:08:46