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

TypeScript如何定义带visible属性节点的对象点分路径联合类型?

问题:为DOM树可见性对象的点分路径参数定义TypeScript类型

场景与需求

有一个表示DOM树可见性的对象:

const visibilities = {
    food: {
        visible: true,
        fruit: {
            visible: true,
            apple: {
                visible: false
            }
        },
        snack: {visible: false}
    }
}

需要实现工具函数getVisibilities,通过点分路径字符串获取指定节点的可见性,调用方式如下:

getVisibilities(visibilities, 'food.fruit.apple')

核心需求是为函数的第二个参数(点分路径字符串)定义TypeScript类型,确保只能传入合法的节点路径。

尝试的方案及问题

尝试编写了如下递归类型定义:

type VisibilityString<Prop extends {[key: string]: any}> = {[K in keyof Prop]: Prop[K] 
   extends {visible: boolean} ? K 
     extends string ? K | `${K}.${VisibilityArray<Prop[K]>}`: never : never}[keyof Prop]

但出现错误:TypeError: VisibilityArray<Prop[K]> is not a string。将参数改为数组形式后,在TypeScript 4.4.4版本中仍存在类型推导错误。

解决方案

1. 点分路径字符串类型实现

通过递归类型筛选合法节点并拼接路径:

// 筛选对象中所有值包含visible属性的键
type VisibilityNodeKeys<T> = {
  [K in keyof T]: T[K] extends { visible: boolean } ? K : never;
}[keyof T];

// 递归生成合法的点分路径字符串
type VisibilityPath<T> = VisibilityNodeKeys<T> extends infer K
  ? K extends string
    ? T[K] extends { visible: boolean }
      ? K | `${K}.${VisibilityPath<T[K]>}`
      : never
    : never
  : never;

// 工具函数定义
function getVisibilities<T extends Record<string, any>>(
  obj: T,
  path: VisibilityPath<T>
): boolean {
  const keys = path.split('.');
  let current: any = obj;
  for (const key of keys) {
    current = current[key];
    if (!current) return false; // 路径不存在时返回默认值,可按需调整
  }
  return current.visible;
}

类型工作原理:

  • VisibilityNodeKeys先过滤出对象中所有值包含visible属性的键,排除无效节点
  • VisibilityPath递归拼接路径:对于每个合法键,既可以单独作为路径,也可以拼接该键与子节点的合法路径
  • TypeScript会自动推导所有合法路径,比如'food'、'food.fruit'、'food.fruit.apple'、'food.snack',非法路径会直接触发类型报错。

2. 数组形式路径的兼容实现(适配TS 4.4.4)

如果需要使用数组形式的路径,可采用以下兼容TS 4.4.4的类型定义:

// 筛选合法节点键
type VisibilityNodeKeys<T> = {
  [K in keyof T]: T[K] extends { visible: boolean } ? K : never;
}[keyof T];

// 递归生成数组形式的合法路径
type VisibilityPathArray<T> = VisibilityNodeKeys<T> extends infer K
  ? K extends string
    ? T[K] extends { visible: boolean }
      ? [K] | [K, ...VisibilityPathArray<T[K]>]
      : never
    : never
  : never;

// 数组形式的工具函数
function getVisibilitiesArray<T extends Record<string, any>>(
  obj: T,
  path: VisibilityPathArray<T>
): boolean {
  let current: any = obj;
  for (const key of path) {
    current = current[key];
    if (!current) return false;
  }
  return current.visible;
}

该写法通过元组递归拼接路径,避免了早期TS版本中字符串模板递归的兼容性问题。

内容的提问来源于stack exchange,提问作者Clifford B. Wolfe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 10:16:22