TypeScript条件类型中推断类型为何回退到默认分支?
解决express-validator Schema类型推断失败的问题
问题根源
你的类型推断逻辑在处理多类型字段时失效,核心原因是:当InferSchema中的TCurrentKey为keyof TSchema的联合类型时,TSchema[TKey]会变成所有字段ParamSchema的联合类型。此时InferPrimitive中的条件判断(比如'isString' extends keyof T)只有当联合类型里的所有ParamSchema都包含对应键时才会成立,而你的场景中不同字段有不同校验键(如isString/isInt/isObject),导致所有条件分支都不满足,最终落入默认的'ITS FAILING HERE'分支。
修复方案
修改类型推断逻辑,确保每个字段的ParamSchema被单独处理,而非作为联合类型判断。以下是调整后的完整代码:
import { Schema, ParamSchema } from 'express-validator'; // 推断单个ParamSchema的基础类型 type InferPrimitive<T extends ParamSchema> = T extends { isString: any } ? string : T extends { isEmail: any } ? string : T extends { isAlpha: any } ? string : T extends { isAlphanumeric: any } ? string : T extends { isBoolean: any } ? boolean : T extends { isInt: any } ? number : T extends { isObject: any } ? {} : never; // 处理可选/可空逻辑 type InferOptional<T extends ParamSchema> = 'optional' extends keyof T ? T['optional'] extends { options: { nullable: true } } ? InferPrimitive<T> | null | undefined : InferPrimitive<T> | undefined : InferPrimitive<T>; // 处理isIn枚举类型 type InferIsIn<T extends ParamSchema> = T extends { isIn: { options: [infer U extends readonly any[]] } } ? U[number] : never; // 推断单个ParamSchema的最终类型 type InferParam<T extends ParamSchema> = T extends { isIn: any } ? InferIsIn<T> : InferOptional<T>; // 将路径字符串转换为嵌套对象类型 type PathToType<Path extends string, T> = Path extends `${infer K}.${infer Rest}` ? { [key in K]: PathToType<Rest, T> } : { [key in Path]: T }; // 合并多个嵌套对象类型为交叉类型 type MergePaths<T extends Schema> = { [K in keyof T]: PathToType<K & string, InferParam<T[K]>> }[keyof T]; // 最终推断类型:将交叉类型合并为完整对象 type Infer<T extends Schema> = MergePaths<T> extends infer O ? { [K in keyof O]: O[K] } : never; // 测试用Schema export const schema = { name: { isString: true, optional: { options: { nullable: true } }, }, age: { isInt: true, }, address: { isObject: true, }, 'address.address1': { isString: true, }, 'address.address2': { isString: true, }, 'address.number': { isInt: true, }, 'address.city.name': { isIn: { options: [['New York', 'Los Angeles'] as const] }, }, } satisfies Schema; // 验证推断结果 const inferred = {} as Infer<typeof schema>; /* 推断出的正确类型: { name: string | null | undefined; age: number; address: { address1: string; address2: string; number: number; city: { name: "New York" | "Los Angeles"; }; }; } */
关键调整点
- 修改
InferPrimitive的判断逻辑:从基于keyof的条件判断改为直接用T extends { [key]: any }的形式,这样即使T是联合类型的一部分,单个成员也能正确匹配对应的类型分支。 - 重构
InferSchema为MergePaths:通过映射类型遍历每个单独的键,将每个路径转换为对应的嵌套对象类型,再通过交叉类型合并所有嵌套对象,最后转换为完整的扁平嵌套对象。 - 确保单个字段的独立处理:每个字段的
ParamSchema被单独传入InferParam进行推断,避免了联合类型导致的条件判断失效问题。
内容的提问来源于stack exchange,提问作者PedroLzOliveira
相关产品推荐
相关产品推荐

