📊 页面导航

适用角色与上手难度

角色推荐度上手难度
🛠️ 开发★★★★★★★★☆☆
🧪 测试★★★☆☆★★★★☆
📦 产品★★☆☆☆★★★★★

🎯 学习产出: 掌握 TypeScript LSP 集成配置和 9 种语义操作,能独立完成跨文件精确重构和调用链分析

🚀 AI 能力提升: 跨文件重构、调试诊断

TypeScript LSP 使用技巧

预计阅读时间: 10 分钟

从 grep 文本搜索升级到语义级代码理解——查询速度从 45 秒降到 50 毫秒,准确率从 95% 升到 100%。

为什么用 LSP

Claude Code 有 Grep 和 Glob 工具,它们做文本匹配——搜字符串 "getUser" 会命中 getUsergetUserNamegetUserById// getUser is deprecated。LSP 做语义理解——搜 getUser 函数的所有调用方,不会误中注释或同名变量。

CircleCI 在 vuejs/core(~149K 行 TypeScript)上的实测

指标LSPGrep
速度~50ms45+ 秒
Token 消耗6,28012,764
准确率(Sonnet)100%~95%(漏 11/260)
成本(Opus)$0.50$0.62

关键结论:LSP 不仅更快更准,还更省 token——而且模型越弱收益越大,LSP 给弱模型兜底。

快速安装

# 1. 开启 LSP 支持(加到 ~/.bashrc 或 ~/.zshrc)
export ENABLE_LSP_TOOL=1

# 2. 安装 TypeScript 语言服务器(推荐 vtsls)
npm install -g @vtsls/language-server typescript

# 3. 在 Claude Code 中安装插件
/plugin install typescript-lsp@claude-plugins-official

# 4. 重载
/reload-plugins

要求:Claude Code ≥ v2.0.74。

核心操作速查

Claude Code 内置 LSP 工具提供 9 种操作,按场景选用:

操作做什么什么时候用
goToDefinition跳转到符号定义修改陌生代码前,先看清楚定义
findReferences找到所有引用重构/重命名前,评估影响范围
goToImplementation找到接口实现面向接口编程时找到具体实现
hover类型信息 + 文档 + 签名理解 API 用法
documentSymbol列出文件中所有符号快速浏览陌生大文件
workspaceSymbol跨文件搜索符号找到和某功能相关的所有代码
incomingCalls找到调用方"谁在调这个函数?"
outgoingCalls找到被调用方"这个函数内部调了谁?"
diagnostics获取编译错误和警告写完代码立即检查

决策树

你需要什么?
├─ 符号的定义或实现        → LSP: goToDefinition / goToImplementation
├─ 符号的所有用法          → LSP: findReferences
├─ 类型信息、文档、签名    → LSP: hover
├─ 调用链或依赖关系        → LSP: incomingCalls / outgoingCalls
├─ 文本搜索(TODO、字符串) → Grep
└─ 文件名匹配              → Glob

三条铁律

来自社区最佳实践(zircote/lsp-tools),每一条都针对 AI 编码最常见的错误模式:

铁律 1:禁止在没看定义前修改陌生代码

❌ Claude 看到一行 import { maskPhone } from '@/utils/phone',
   直接改了 maskPhone 的调用方式——但它不知道这个函数的签名
✅ 先用 goToDefinition 看 maskPhone 的定义,
   确认参数类型、返回值、副作用,然后才改调用方

铁律 2:禁止在没做引用分析前重构

❌ Claude 重命名了一个 export 的函数名,只改了定义,
   没改 12 个调用方——构建全炸
✅ 改名前先用 findReferences 找到所有引用,
   逐一确认改动范围,再批量修改

铁律 3:禁止声称"代码没问题"而不跑 LSP 诊断

❌ Claude: "这个组件应该没问题了"
   实际上有 3 个类型错误,tsserver 早就报了
✅ 每次改动后跑 diagnostics 确认 0 错误,
   有错先修再继续

实战用例

用例 1:理解陌生 API

打开一个不熟悉的项目,看到一行 useQuery 但不知道从哪来的:

> 对 useQuery 做 hover,看它的类型签名和文档

LSP 返回:函数签名、参数类型、返回值类型、JSDoc 注释。不用翻 node_modules 源码。

用例 2:安全地重命名

要把 getUserList 重命名为 fetchUsers

> 1. findReferences getUserList — 看所有调用方
> 2. 确认 8 个文件,3 个 .ts,5 个 .vue
> 3. 逐个文件改名,保证没有遗漏

grepping getUserList 会漏掉通过别名 import 的或在 as 子句里的引用——LSP 不会漏。

用例 3:追踪接口实现

interface PaymentGateway {
  charge(amount: number): Promise<ChargeResult>;
}
// 哪些类实现了这个接口?
> goToImplementation PaymentGateway

返回:StripeGatewayAlipayGatewayWechatPayGateway——直接看到所有实现。

用例 4:影响分析

要改 User 类型的一个字段从 name: stringname: { first: string, last: string }

> 1. findReferences User.name — 13 个引用
> 2. incomingCalls 分析哪些函数依赖 User.name 的 string 类型
> 3. 逐个修改,每次改完跑 diagnostics

用例 5:快速预览大文件

打开一个 800 行的 OrderService.ts

> documentSymbol OrderService.ts

LSP 返回这个文件的结构——class、method、property 的树形列表。不用翻 800 行,一眼看到所有方法。

用例 6:跨文件导航

看到一个 formatDate 调用,但项目里有 3 个 formatDate

> goToDefinition formatDate

LSP 根据类型推断跳转到正确的那个——不是字符串匹配。grep 会把 3 个都列出来让你猜。

配置优化

.lsp.json(项目根目录)

{
  "typescript": {
    "command": "vtsls",
    "args": ["--stdio"],
    "extensionToLanguage": {
      ".ts": "typescript",
      ".tsx": "typescriptreact",
      ".js": "javascript",
      ".jsx": "javascriptreact",
      ".mts": "typescript",
      ".cts": "typescript",
      ".mjs": "javascript",
      ".cjs": "javascript"
    }
  }
}

tsconfig.json 加速

{
  "compilerOptions": {
    "skipLibCheck": true,
    "incremental": true
  },
  "exclude": ["node_modules", "dist", "build", ".next", "coverage"]
}

skipLibCheck: true 跳过 node_modules 的类型检查——大部分项目不需要检查依赖的类型。incremental: true 启用增量编译,后续检查更快。

为什么用 vtsls 而不是 typescript-language-server

vtslstypescript-language-server
维护状态活跃较慢
速度更快一般
Vue SFC 支持@vue/typescript-plugin 完美需要额外配置
内存占用更低略高

Hook 自动化

配置好 LSP 后再加 hook,让 Claude Code 每次改完文件自动检查:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write ${CLAUDE_FILE_PATHS}"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "npx tsc --noEmit 2>&1 | tail -5"
          }
        ]
      }
    ]
  }
}

效果:每次 Write/Edit 后自动格式化,每次"完成"前自动 typecheck。零人工干预。

Vue + TypeScript 专项

Vue 单文件组件(.vue)需要特殊处理:

方案一:vtsls + Vue 插件

npm install -g @vtsls/language-server @vue/typescript-plugin

.lsp.json 增加 .vue 映射:

{
  "typescript": {
    "command": "vtsls",
    "args": ["--stdio"],
    "extensionToLanguage": {
      ".vue": "typescript",
      ".ts": "typescript"
    }
  }
}

方案二:vue-ts-lsp(专用代理)

npx vue-ts-lsp install cc

vue-ts-lsp 是专门为 Vue + TypeScript 做的 LSP 代理,自动处理 .vue 文件的 template 和 script 分离。

Vue LSP 常见场景

# 在 .vue 文件中查找 ref/reactive 变量的定义
> goToDefinition status
# → 跳转到同一个 <script setup> 里的 const status = ref('')
# → 不跳到其他文件的同名 status

# 查找一个 prop 的所有使用位置
> findReferences props.options
# → 找到 template 里的 v-for="o in options" 和 script 里的 props.options.filter()
# → 覆盖 template 和 script 两个上下文

故障排除

问题原因解决
LSP 不启动tsconfig.json 不存在项目根目录建 tsconfig.json
无诊断输出ENABLE_LSP_TOOL 未设置export ENABLE_LSP_TOOL=1
响应慢node_modules 被扫描tsconfig.json 中 exclude node_modules
import 后无提示依赖未安装npm install
切分支后符号过期LSP 未感知分支变更"Restart the LSP server"
Vue 文件无提示LSP 未关联 .vue配置 extensionToLanguage 或安装 vue-ts-lsp

与 codegraph 互补

LSP 和 codegraph 定位不同,搭配使用:

LSPcodegraph
数据类型实时语法树 + 类型预建知识图谱
更新方式文件保存即更新文件变化 ~1 秒后更新
强项类型推导、诊断、定义跳转调用链追踪、影响分析、跨文件探索
典型用法改代码时的即时反馈理解架构时的全局检索

最佳组合:改代码时用 LSP(实时类型检查 + 定义跳转),理解代码架构时用 codegraph(调用链追踪 + 依赖图谱)。


相关页面