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

TypeScript如何实现支持自动推断的嵌套泛型构建类型安全特性检查器

实现类型安全的特性配置检查器

问题核心

无法推导类型的根本原因是:给config变量手动标注了宽泛的FeatureConfig类型后,TypeScript会擦除所有子节点的键名、isActivated具体布尔值的字面量类型,只保留通用的字符串索引、boolean类型,自然无法推导链式调用的属性类型。

要实现完整的类型安全,需要做两件事:

  • 保留配置对象的字面量类型信息,不要用宽泛类型标注擦除细节
  • 编写递归泛型类型,根据配置结构推导返回的checker类型,同时处理「父节点未激活时所有子节点默认未激活」的逻辑

完整实现

1. 调整配置定义

不要给配置对象手动标注FeatureConfig类型,配合as const锁定所有字面量类型,避免类型拓宽:

// 删掉 : FeatureConfig 标注,否则会丢失所有类型细节
const config = {
   isActivated: true,
   childs: {
      bar: {
         isActivated: true,
         childs: {
            foo: {
              isActivated: true
            }
         }
      },
      foo: {
         isActivated: false,
         childs: {
            bar: {
              isActivated: true
            }
         }
      }
   }
} as const

2. 定义类型工具

首先保留基础配置的类型约束,再写递归的类型推导工具,处理父子节点的激活状态继承:

// 基础配置结构约束,仅用于泛型参数限制
type BaseFeatureConfig = {
  isActivated: boolean;
  childs?: Record<string, BaseFeatureConfig>
}

/**
 * 递归推导特性检查器的类型
 * @param T 当前节点的配置类型
 * @param ParentActivated 父节点是否激活,父节点未激活时当前及所有子节点均为未激活
 */
type FeatureChecker<T extends BaseFeatureConfig, ParentActivated extends boolean = true> = {
  isActivated: ParentActivated extends false ? false : T['isActivated']
} & (
  T['childs'] extends Record<string, BaseFeatureConfig>
    ? { [K in keyof T['childs']]: FeatureChecker<T['childs'][K], ParentActivated extends false ? false : T['isActivated']> }
    : unknown
)

3. 实现构建函数

给函数加泛型约束,运行时逻辑可以直接用你原本写的JS实现,这里给个参考实现:

const createFeatureChecker = <T extends BaseFeatureConfig>(config: T): FeatureChecker<T> => {
  const createNode = (nodeConf: BaseFeatureConfig, parentActive: boolean): any => {
    const currentActive = parentActive && nodeConf.isActivated
    const node: Record<string, any> = { isActivated: currentActive }

    if (nodeConf.childs) {
      for (const [key, childConf] of Object.entries(nodeConf.childs)) {
        node[key] = createNode(childConf, currentActive)
      }
    }
    return node
  }

  return createNode(config, true)
}

效果验证

使用时TS会自动推导所有链式调用的类型,完全匹配预期:

const featureChecker = createFeatureChecker(config);

featureChecker.bar.foo.isActivated // 类型推导为字面量true,运行时值为true
featureChecker.foo.isActivated // 类型推导为字面量false,运行时值为false
featureChecker.foo.bar.isActivated // 类型推导为字面量false,运行时值为false(父节点foo未激活,子节点自动失效)

注意事项

  • 不要给传入的配置对象手动标注宽泛的FeatureConfig类型,否则会丢失所有字面量信息,导致类型推导失败。如果需要校验配置格式是否正确,可以单独写类型校验工具,不要直接给变量赋值宽泛类型。
  • as const是必要的,否则TS会把true/false拓宽为boolean类型,无法推导出具体的激活状态字面量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:09:47