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

如何实现泛型化TypeScript嵌套类型的指定值类型点路径验证

泛型化嵌套对象的点格式路径类型验证

需求说明

现有对象类型:

type exampleType = {
    propertyOne: string
    propertyTwo: number,
    propertyThree: {
        propertyFour: string,
        propertyFive: Date,
        propertySix: boolean,
    }
}

需要实现一个类型,用来验证点格式字符串,确保它仅指向值类型为string或Date的路径,最终期望的编译结果为:

propertyOne | propertyThree.propertyFour | propertyThree.propertyFive

已实现的基础版本

借助已有方案,已经实现了仅针对字符串类型路径的验证:

type PathsToStringProps<T> = T extends string ? [] : {
    [K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K]>]
}[Extract<keyof T, string>];

type Join<T extends string[], D extends string> =
    T extends [] ? never :
    T extends [infer F] ? F :
    T extends [infer F, ...infer R] ?
    F extends string ? 
    `${F}${D}${Join<Extract<R, string[]>, D>}` : never : string;  

type Path = Join<PathsToStringProps<exampleType>, ".">

泛型化遇到的问题

希望将方案泛型化,让Path接受两个泛型参数:T(对应示例中的exampleType)和V(对应示例中的string|Date)。

  • 泛型化T时,最初的定义type Path<T> = Join<PathsToStringProps<T>, ".">出现Excessive stack depth comparing types 'PathsToStringProps<T>' and 'string[]'.ts(2321)错误,通过指定T extends {[key: string]: any}解决了该问题。
  • 但添加V参数限制路径指向的值类型时,如下代码出现类型错误:
type PathsToStringProps<T, V> = T extends (V) ? [] : {
    [K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K], V>]
}[Extract<keyof T, string>];

type Join<T extends string[], D extends string> =
    T extends [] ? never :
    T extends [infer F] ? F :
    T extends [infer F, ...infer R] ?
    F extends string ? 
    `${F}${D}${Join<Extract<R, string[]>, D>}` : never : string;  

type Path<T extends {[key: string]: any}, V> = Join<PathsToStringProps<T, V>, ".">
  • 若移除Path的泛型参数V,仅在PathsToStringProps中写死V为string|Date,错误则消失:
type Path<T extends {[key: string]: any}> = Join<PathsToStringProps<T, string|Date>, ".">

解决方案

问题出在递归类型的约束和泛型参数传递上,需要给递归类型添加更明确的分支判断,避免TypeScript无法正确推断递归终止条件。修正后的完整代码如下:

type PathsToValueType<T, V> = 
  // 终止条件:当前类型匹配目标值类型,返回空数组标记路径结束
  T extends V ? [] : 
  // 仅对对象类型递归遍历键,非对象且不匹配V的类型直接排除
  T extends object ? {
    [K in Extract<keyof T, string>]: [K, ...PathsToValueType<T[K], V>]
  }[Extract<keyof T, string>] : 
  never;

type Join<T extends string[], D extends string> =
  T extends [] ? never :
  T extends [infer F] ? F extends string ? F : never :
  T extends [infer F, ...infer R] ? 
    F extends string ? `${F}${D}${Join<Extract<R, string[]>, D>}` : never :
  never;

type Path<T extends Record<string, any>, V> = Join<PathsToValueType<T, V>, ".">;

// 测试示例
type exampleType = {
    propertyOne: string
    propertyTwo: number,
    propertyThree: {
        propertyFour: string,
        propertyFive: Date,
        propertySix: boolean,
    }
};

type ValidPaths = Path<exampleType, string | Date>;
// 结果:"propertyOne" | "propertyThree.propertyFour" | "propertyThree.propertyFive"

修正说明

  1. 给PathsToValueType增加了T extends object的分支判断,明确仅对对象类型进行递归遍历,避免非对象类型触发不必要的递归。
  2. 补充了非对象且不匹配目标类型的分支,返回never直接排除这类无效路径,让类型推断逻辑更清晰。
  3. 优化Join类型的推断逻辑,增加对路径片段的显式字符串类型判断,避免潜在的类型兼容性问题。

内容的提问来源于stack exchange,提问作者Ali Bdeir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 12:45:44