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

TypeScript泛型函数中'schema'字段无法索引输出类型的类型错误问题

TypeScript泛型函数中'schema'字段无法索引输出类型的类型错误问题

我明白你遇到的问题了——你希望通过项目名和输出名索引到对应的schema,进而对传入的data做编译时类型校验,但TypeScript却报错说"schema"无法索引到目标类型。这主要是因为TypeScript的泛型推断没有足够信息来确定ProjectOutputs[TProj][TOutputName]一定包含schema字段,尤其是当ProjectOutputs中存在未定义的项目(比如你的proj2)时,TypeScript会认为ProjectOutputs[TProj]可能是undefined,无法细化到具体的Output类型。

下面是具体的解决方案:

方案一:通过NonNullable和泛型约束细化类型

我们可以用NonNullable排除项目不存在的情况,同时约束输出名必须是已有项目的有效键,让TypeScript明确知道对应的输出类型一定包含schema字段:

type Project = 'proj1' | 'proj2';
type Output = { foo: string; bar: string; schema: string };

// 调整satisfies的类型,确保每个项目对应Record<string, Output>(可选)
export const projectOutputs = {
  proj1: {
    entity_map_of_id_to_name: {
      foo: "entity_map_of_id_to_name.json",
      bar: "data/auto/entity_map_of_id_to_name.json",
      schema: '...schema 1...' as const
    },
    entity_map_of_name_to_id: {
      foo: "entity_map_of_lc_name_to_id.json",
      bar: "data/auto/entity_map_of_name_to_id.json",
      schema: '...schema 2...' as const
    }
  }
} satisfies Partial<Record<Project, Record<string, Output>>>;

export type ProjectOutputs = typeof projectOutputs;

// 模拟Zod的Infer类型(实际项目替换为z.infer<T>)
type InferFromSchema<T> = T;

function writeOutput<
  // 约束TProj为ProjectOutputs中存在的项目键
  TProj extends keyof ProjectOutputs,
  // 约束TOutputName为该项目下的有效输出键
  TOutputName extends keyof NonNullable<ProjectOutputs[TProj]>
>(
  project: TProj,
  outputName: TOutputName,
  // 此时TypeScript能明确输出类型包含schema字段
  data: InferFromSchema<NonNullable<ProjectOutputs[TProj]>[TOutputName]['schema']>
) {
  // 运行时兜底检查,确保项目和输出存在
  const projectOutput = projectOutputs[project];
  if (!projectOutput) throw new Error(`项目 ${project} 未定义`);
  const output = projectOutput[outputName];
  if (!output) throw new Error(`项目 ${project} 中不存在输出 ${outputName}`);

  // 你的业务逻辑实现
  console.log(`写入 ${project} 的 ${outputName}:`, data);
}

// 测试:类型校验通过
writeOutput('proj1', 'entity_map_of_id_to_name', '符合schema1的数据');
// 测试:传入不存在的输出名会编译报错
// writeOutput('proj1', 'invalid_output', '错误数据');
// 测试:传入未定义的项目会编译报错
// writeOutput('proj2', 'any_output', '错误数据');

方案二:通过条件类型强制检查schema字段

如果需要更灵活的约束(比如允许输出有不同结构但必须包含schema),可以用条件类型明确提取schema类型:

function writeOutput<
  TProj extends keyof ProjectOutputs,
  TOutputName extends keyof ProjectOutputs[TProj]
>(
  project: TProj,
  outputName: TOutputName,
  // 用条件类型判断是否存在schema字段,存在则提取类型,否则返回never(编译报错)
  data: ProjectOutputs[TProj][TOutputName] extends { schema: infer TSchema }
    ? InferFromSchema<TSchema>
    : never
) {
  // 业务逻辑实现
}

为什么原代码会报错?

在你的原始代码中,ProjectOutputs[TProj]可能是undefined(比如TProj为proj2时),TypeScript无法确定ProjectOutputs[TProj][TOutputName]一定是Output类型,因此无法保证它有schema字段。通过NonNullable和更严格的泛型约束,我们给TypeScript足够的类型信息,从而解决了索引错误。

最终效果

现在调用writeOutput时会实现以下编译时校验:

  • 传入不存在的项目名会直接报错;
  • 传入项目中不存在的输出名会直接报错;
  • 传入的data类型会自动关联到对应输出的schema类型(实际项目中替换InferFromSchema为z.infer后,就能实现Zod schema的自动类型推断)。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 08:14:31