类型检查插件与 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. 工作流程
编辑器打开
.ts文件LSP 客户端连接
tsservertsserver解析项目,构建语法树和符号表监听文件变化,增量更新类型信息
返回诊断、补全等信息给编辑器
关键:
tsserver是增量构建的,比tsc全量编译快得多。
目标二:使用 tsc --extendedDiagnostics 分析编译性能
当构建缓慢时,使用:
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. 创建一个简单插件
// 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 中启用
{
"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.json2. 子项目配置(如 /core/tsconfig.json)
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "dist"
},
"include": ["src"]
}composite: true 是 Project Reference 的必要条件
3. 主项目引用(如 /app/tsconfig.json)
{
"compilerOptions": {
"composite": false,
"outDir": "dist"
},
"references": [
{ "path": "../core" },
{ "path": "../utils" }
],
"include": ["src"]
}4. 构建命令
# 构建所有引用项目
tsc -b
# 增量构建
tsc -b --incremental
# 清理
tsc -b --clean5. 优势
| 优势 | 说明 |
|---|---|
| 并行构建 | 多个子项目可并行编译 |
| 增量构建 | 只重新编译变更的部分 |
| 类型隔离 | 避免类型污染 |
| 更清晰的依赖 | 显式声明项目依赖 |
| 更快的编辑器响应 | 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 项目的引用。
{
"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 高级工程化的关键一步。