Skip to content

类型检查插件与 LSP

核心问题解析

问题一:能不能自定义类型规则?

TypeScript 内置的类型检查无法满足所有场景:

  • 禁止使用 any
  • 强制 API 文档注释
  • 检查模块导入规范(如禁止跨层引用)
  • 自定义类型安全规则(如状态机合法性)

答案:虽然不能直接修改 tsc,但可以通过以下方式扩展:

  • TypeScript Plugin:扩展语言服务,提供自定义诊断

  • Linters(如 ESLint):结合 @typescript-eslint 实现语法规则检查

  • 自定义类型检查工具:基于 TypeScript Compiler API 编写

  • tsc 本身不支持“自定义类型规则”,但 Language Server 可以通过插件注入诊断信息。

问题二:编辑器提示是怎么来的?

你在 VS Code 中看到的:

  • 类型悬停提示
  • 自动补全(IntelliSense)
  • 错误波浪线
  • 跳转定义、查找引用
  • 重构建议

这些功能并非来自 tsc 编译过程,而是来自 TypeScript Language Server (TSServer)

答案:编辑器通过 LSP(Language Server Protocol)tsserver 通信,实时获取类型信息。

问题三:如何提升大型项目的类型性能?

大型项目常见问题:

  • tsc --watch 启动慢
  • 编辑器卡顿
  • 类型检查耗时长
  • 全量构建时间过长

答案

  • 使用 Project References
  • 优化 tsconfig.json 结构
  • 使用 tsc --extendedDiagnostics 分析瓶颈
  • 合理拆分项目,避免“单体 tsconfig”

学习目标详解

目标一:理解 TypeScript Language Server 工作机制

1. LSP(Language Server Protocol)

  • 由 Microsoft 提出的标准协议
  • 实现编辑器语言服务的解耦
  • VS Code、Vim、Sublime 等均可通过 LSP 使用 TS 语言服务
[Editor] ←LSP→ [tsserver]

2. tsserver 核心能力

tsserver 是 TypeScript 的语言服务后台进程,提供:

功能说明
类型检查实时诊断语法/类型错误
IntelliSense自动补全、参数提示
导航跳转定义、查找引用
重构重命名、提取方法
格式化基于 tsfmt 规则
快速修复“添加缺失的属性”等建议

3. 工作流程

  1. 编辑器打开 .ts 文件

  2. LSP 客户端连接 tsserver

  3. tsserver 解析项目,构建语法树和符号表

  4. 监听文件变化,增量更新类型信息

  5. 返回诊断、补全等信息给编辑器

  6. 关键tsserver增量构建的,比 tsc 全量编译快得多。

目标二:使用 tsc --extendedDiagnostics 分析编译性能

当构建缓慢时,使用:

bash
tsc --extendedDiagnostics

输出详细性能指标:

Files:          1234
Lines:         56789
Nodes:        123456
Identifiers:   45678
Symbols:       34567
Types:         12345
Memory used: 1234567K
I/O read:      100.00ms
I/O write:      10.00ms
Parse time:    200.00ms
Bind time:      50.00ms
Check time:    800.00ms  ← 类型检查耗时
Emit time:     100.00ms
Total time:   1160.00ms

重点关注

  • Check time:类型检查时间,过长说明类型复杂或存在性能陷阱
  • Memory used:内存占用,过高可能需拆分项目

常见性能陷阱:

  • 过度复杂的条件类型(如深层递归)
  • 大量联合类型的分布式条件
  • any 泛滥导致类型推断失效
  • 单一文件过大

目标三:了解 typescript-plugin 扩展类型检查能力

TypeScript 支持通过插件扩展语言服务功能。

1. 创建一个简单插件

ts
// my-plugin.ts
export = function init(modules) {
  const { typescript: ts } = modules;

  return {
    create(info) {
      info.project.projectService.logger.info('My plugin loaded!');

      const proxy = Object.create(null);
      for (const k of Object.keys(info.languageService))
        proxy[k] = info.languageService[k];

      proxy.getCompletionsAtPosition = (fileName, position, options) => {
        const prior = info.languageService.getCompletionsAtPosition(fileName, position, options);
        // 可以注入自定义补全项
        return prior;
      };

      proxy.getSemanticDiagnostics = (fileName) => {
        const prior = info.languageService.getSemanticDiagnostics(fileName);
        // 注入自定义诊断(错误/警告)
        const customDiag = [{
          file: info.project.getSourceFile(fileName),
          start: 0,
          length: 1,
          messageText: "Custom rule: don't use 'any'",
          category: ts.DiagnosticCategory.Error,
          code: 10001
        }];
        return [...prior, ...customDiag];
      };

      return proxy;
    }
  };
};

2. 在 tsconfig.json 中启用

json
{
  "compilerOptions": {
    "plugins": [
      { "name": "my-plugin" }
    ]
  }
}

3. 实际应用案例

  • typescript-eslint:通过插件在语言服务中运行 ESLint 规则
  • ts-plugin-css-modules:为 CSS Modules 生成类型
  • graphql-codegen:为 GraphQL 查询提供类型补全
  • angular language service:为 Angular 模板提供类型检查

目标四:实践项目引用(Project References)优化大型项目构建

大型项目应拆分为多个子项目,使用 Project References

1. 项目结构

/my-monorepo
  /packages
    /core
      tsconfig.json
    /utils
      tsconfig.json
    /app
      tsconfig.json

2. 子项目配置(如 /core/tsconfig.json

json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "outDir": "dist"
  },
  "include": ["src"]
}

composite: true 是 Project Reference 的必要条件

3. 主项目引用(如 /app/tsconfig.json

json
{
  "compilerOptions": {
    "composite": false,
    "outDir": "dist"
  },
  "references": [
    { "path": "../core" },
    { "path": "../utils" }
  ],
  "include": ["src"]
}

4. 构建命令

bash
# 构建所有引用项目
tsc -b

# 增量构建
tsc -b --incremental

# 清理
tsc -b --clean

5. 优势

优势说明
并行构建多个子项目可并行编译
增量构建只重新编译变更的部分
类型隔离避免类型污染
更清晰的依赖显式声明项目依赖
更快的编辑器响应tsserver 只加载当前项目

总结:本节核心要点

技术用途工具/配置
LSP / tsserver编辑器智能提示VS Code 内置
--extendedDiagnostics分析编译性能tsc --extendedDiagnostics
TypeScript Plugin扩展语言服务compilerOptions.plugins
Project References大型项目优化references + composite

最佳实践

新项目从一开始就使用 Project References--extendedDiagnostics 定期检查构建性能 通过 ESLint + typescript-plugin 实现自定义规则 理解 LSP 机制,避免误以为 tsc = 编辑器提示来源

练习题

练习题 1:解读 --extendedDiagnostics 输出

如果 Check time 占总时间 90%,可能原因是什么?

答案:类型检查复杂度过高,可能是:

  • 存在深层递归类型
  • 大量联合类型条件判断
  • 类型推断链过长
  • 应优化类型定义或拆分文件

练习题 2:启用 Project Reference

app 项目添加对 shared 项目的引用。

json
{
  "references": [
    { "path": "../shared" }
  ]
}

确保 shared/tsconfig.json 包含 "composite": true

练习题 3:LSP 作用

LSP 解决了什么问题?

答案:实现了编辑器语言服务的解耦,让多种编辑器都能使用同一语言服务,提升开发体验一致性。

练习题 4:Plugin 用途

typescript-plugin 可以用于:

  • 为 CSS Modules 生成类型
  • 在编辑器中显示自定义警告
  • 替换 tsc 的类型检查逻辑 不能替换,只能扩展)

练习题 5:composite 选项

为什么 Project Reference 中的子项目必须设置 "composite": true

答案:composite: true 启用以下能力:

  • 增量编译支持
  • 生成 .tsbuildinfo 文件
  • 允许被其他项目引用
  • 强制输出声明文件(.d.ts

掌握类型检查插件与 LSP 机制,你不仅能优化大型项目的构建性能,还能深入理解现代编辑器的智能提示原理, 并具备扩展 TypeScript 能力的高级技能。这是迈向 TS 高级工程化的关键一步。