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

如何使用ts-morph移除未使用的对象字面量属性?

检测并移除lang对象中未使用的属性

你可以借助ts-morph的引用查找API实现这个需求,核心思路是遍历lang对象的每个属性,查找整个项目中对该属性的所有引用,确认无引用后移除属性。以下是具体实现方案:

核心实现代码

const project = new Project({ tsConfigFilePath: 'tsconfig.webpack.json' });
const source = project.addSourceFileAtPathIfExists('constants.ts');
if (!source) return;

const messages = source.getVariableDeclarationOrThrow('lang');
const value = messages.getInitializerOrThrow() as ObjectLiteralExpression;

// 检查属性是否被使用的工具函数
function checkPropertyUsage(rootNode: VariableDeclaration, propPath: string): boolean {
  const references = rootNode.findReferences();
  for (const ref of references) {
    for (const refNode of ref.getReferences()) {
      const node = refNode.getNode();
      let currentPath = '';
      let currentParent = node.parent;

      // 追踪属性访问链,比如 lang.nested.value 会解析成 "nested.value"
      while (currentParent instanceof PropertyAccessExpression) {
        currentPath = currentParent.getName() + (currentPath ? `.${currentPath}` : '');
        currentParent = currentParent.parent;
      }

      // 检查解构赋值中的属性
      if (currentParent instanceof ObjectLiteralExpression) {
        const prop = currentParent.getProperty(propPath.split('.')[0]);
        if (prop) {
          // 处理嵌套解构,比如 const { nested: { value } } = lang
          if (propPath.includes('.')) {
            const nestedPropPath = propPath.split('.').slice(1).join('.');
            return checkPropertyUsage(rootNode, nestedPropPath);
          }
          return true;
        }
      }

      // 检查直接属性访问或函数调用
      if (currentPath === propPath) {
        // 处理函数调用场景,比如 lang.calculate()
        if (currentParent instanceof CallExpression && currentParent.getCallee() === node.parent) {
          return true;
        }
        return true;
      }
    }
  }
  // 动态属性访问(如 lang[var])无法静态检测,默认视为已使用(可按需调整)
  return false;
}

// 递归遍历所有属性(含嵌套)
function traverseProperties(objExpr: ObjectLiteralExpression, parentPath = '') {
  const properties = objExpr.getProperties();
  for (const prop of properties) {
    if (!(prop instanceof PropertyAssignment)) continue;

    const propName = prop.getName();
    const fullPath = parentPath ? `${parentPath}.${propName}` : propName;
    const initializer = prop.getInitializer();

    // 先处理嵌套对象的子属性
    if (initializer instanceof ObjectLiteralExpression) {
      traverseProperties(initializer, fullPath);
      // 若子属性全被移除,且嵌套对象本身未被直接访问,则移除该嵌套对象
      const isNestedUsed = checkPropertyUsage(messages, fullPath);
      if (!isNestedUsed && initializer.getProperties().length === 0) {
        prop.remove();
      }
    } else {
      // 检查当前属性是否被使用,未使用则移除
      const isUsed = checkPropertyUsage(messages, fullPath);
      if (!isUsed) {
        prop.remove();
      }
    }
  }
}

// 启动遍历处理
traverseProperties(value);

// 保存修改后的文件
source.save();

关键说明

  • 覆盖三种常见使用场景:直接属性访问(lang.hello)、解构赋值(const { hello } = lang/const { nested: { value } } = lang)、函数调用(lang.calculate(10))。
  • 动态属性访问(如lang[dynamicKey])无法通过静态分析检测,代码中默认标记为已使用,可根据项目需求调整这部分逻辑。
  • 嵌套属性处理会先清理子属性,若嵌套对象本身无直接引用且已无子属性,会一并移除。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 14:21:30