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

