构建简单的代码检查工具
使用编译器 API 创建自定义诊断工具
构建简单的代码检查工具 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 4 节课。
什么是代码检查工具
代码检查工具会分析源代码,报告风格或正确性问题。使用 TypeScript 编译器接口,您可以构建 ESLint 插件单独无法实现的、具备类型感知能力的规则。
// Goal: warn when console.log is called in TypeScript files项目设置
创建一个 Node.js 脚本,加载一个 TypeScript program,遍历 AST 并报告诊断信息。
import ts from "typescript";
import path from "path";
const files = ["src/index.ts"];
const program = ts.createProgram(files, { strict: true });定义规则
一个 rule 是接收 AST 节点的函数;如果违反该 rule,它会返回一条可选的诊断消息。
type Diagnostic = { file: string; line: number; message: string };
function noConsoleLog(node: ts.Node, sf: ts.SourceFile): Diagnostic | null {
if (!ts.isCallExpression(node)) return null;
const expr = node.expression.getText(sf);
if (expr !== "console.log") return null;
const { line } = sf.getLineAndCharacterOfPosition(node.pos);
return { file: sf.fileName, line: line + 1, message: "No console.log allowed" };
}遍历 AST
编写一个递归的 walk,对每个源文件中的每个节点应用所有 rules。
function walk(
node: ts.Node,
sf: ts.SourceFile,
rules: ((n: ts.Node, sf: ts.SourceFile) => Diagnostic | null)[]
): Diagnostic[] {
const diags: Diagnostic[] = [];
for (const rule of rules) {
const d = rule(node, sf);
if (d) diags.push(d);
}
ts.forEachChild(node, child => diags.push(...walk(child, sf, rules)));
return diags;
}运行代码检查器
遍历所有源文件,应用 rules,并打印诊断信息。
const rules = [noConsoleLog];
const allDiagnostics: Diagnostic[] = [];
for (const sf of program.getSourceFiles()) {
if (!sf.isDeclarationFile) {
allDiagnostics.push(...walk(sf, sf, rules));
}
}
allDiagnostics.forEach(d =>
console.log(`${d.file}:${d.line} — ${d.message}`)
);添加类型感知规则
使用类型检查器构建依赖表达式 TypeScript 类型的 rules。
const checker = program.getTypeChecker();
function noAnyReturn(node: ts.Node, sf: ts.SourceFile): Diagnostic | null {
if (!ts.isFunctionDeclaration(node)) return null;
const sig = checker.getSignatureFromDeclaration(node);
if (!sig) return null;
const ret = checker.getReturnTypeOfSignature(sig);
if (ret.flags & ts.TypeFlags.Any) {
const { line } = sf.getLineAndCharacterOfPosition(node.pos);
return { file: sf.fileName, line: line + 1, message: "Function returns any" };
}
return null;
}持续集成的退出代码
当发现诊断信息时返回非零退出代码,以便持续集成流水线使构建失败。
if (allDiagnostics.length > 0) {
console.error(`${allDiagnostics.length} lint error(s)`);
process.exit(1);
}
process.exit(0);规则:无未使用变量
编译器接口通过 ts.getPreEmitDiagnostics 提供未使用变量诊断信息,用内置检查增强您的自定义 rules。
const preEmit = ts.getPreEmitDiagnostics(program);
preEmit.forEach(d => {
const msg = ts.flattenDiagnosticMessageText(d.messageText, "
");
console.log(msg);
});规则:要求返回类型
当函数缺少显式返回类型注解时发出警告的规则,有助于强制执行文档规范。
function requireReturnType(node: ts.Node, sf: ts.SourceFile): Diagnostic | null {
if (!ts.isFunctionDeclaration(node)) return null;
if (!node.type) {
const { line } = sf.getLineAndCharacterOfPosition(node.pos);
return { file: sf.fileName, line: line + 1, message: "Missing return type" };
}
return null;
}与 ESLint 比较
您的自定义代码检查器可以补充 ESLint。使用 ESLint 处理风格规则,使用您的 TypeScript 编译器接口工具处理 ESLint 无法表达的类型感知 rules。
// ESLint: stylistic and common pattern rules
// Compiler API linter: deep type reasoning, custom constraints回顾:构建代码检查器
TypeScript 编译器接口代码检查器会创建 program,使用 rules 遍历 AST,可选地查询类型检查器,并在出现错误时以非零状态退出。这种方法可以实现真正具备类型感知能力的代码检查 rules。
快速检查
是什么让编译器接口代码检查器比 ESLint 更有优势?
您学到的内容
您使用编译器接口构建了一个简单的 TypeScript 代码检查器:加载 program,遍历 AST,应用 rules(可选类型感知),并报告诊断信息。这是任何 TypeScript 项目进行自定义静态分析的基础。
常见问题解答
「构建简单的代码检查工具」课时是免费的吗?
是的 — 「构建简单的代码检查工具」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 4 节课。
「构建简单的代码检查工具」这节课中我会学到什么?
使用编译器 API 创建自定义诊断工具 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 TypeScript Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「构建简单的代码检查工具」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 TypeScript Academy 课中编写并运行代码吗?
能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。