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
相关产品推荐
相关产品推荐

