前言:最近在做关于ide中对于typescript相关的类型检查,扒开ts源码拜读了一番,这里做个总结,也算是纪念我们那逝去的秀发。
TypeScript的DNA鉴定中心:源码级解剖现场
(想象这里有个编译器正在给代码做开胸手术)
让我们穿上白大褂,戴上编译器专家的胸牌,直接进入 TypeScript 的源码解剖室。今天,我们要在显微镜下解析 TypeScript 编译器的运行过程,探究它如何分析、检查和变形你的代码。
一、语法解析:源代码的"基因测序"
现场直击 :当你的代码被扔进 tsc
(TypeScript 编译器),它的第一步是对代码进行 词法分析 和 语法解析 ,这就像 CSI 鉴证科在对DNA样本进行测序,拆解基因片段,并拼装成完整的基因组。
typescript
// 来自 TypeScript 源码的简化版解析逻辑
function parseSourceFile(): SourceFile {
const result = createSourceFile();
while (true) {
const token = scanner.scan(); // 词法分析:拆解DNA片段
switch (token) {
case SyntaxKind.LetKeyword:
parseVariableStatement(result);
break;
case SyntaxKind.InterfaceKeyword:
parseInterfaceDeclaration(result);
break;
// ...处理其他语法结构
}
}
return result;
}
解析过程拆解
-
词法分析(Lexical Analysis) :扫描代码,把 const cyberpunk = 2077; 变成 Token 序列 [const] [cyberpunk] [=] [2077] [;],就像 DNA 片段被拆解成碱基对。
-
语法分析(Syntax Analysis) :将这些 Token 组装成 AST(抽象语法树),就像基因拼接成完整染色体。
-
AST 结构:每个 ; 号都像 DNA 终止子,标志着一个语法片段的结束。
AST 实况转播
使用 TypeScript 编译器 API 观察代码的"基因图谱":
typescript
import ts from 'typescript';
const code = `const 赛博朋克 = 2077`;
const ast = ts.createSourceFile('demo.ts', code, ts.ScriptTarget.Latest);
console.log(ast.statements[0]); *// 输出变量声明的基因序列*
它会生成如下 AST 结构:
typescript
VariableStatement {
declarationList: VariableDeclarationList {
declarations: [
VariableDeclaration {
name: Identifier { text: "赛博朋克" }, *// 变量名基因片段*
initializer: NumericLiteral { text: "2077" } *// 数值型DNA*
}
]
}
}
TypeScript 解析器的工作就像 DNA 测序仪,把代码拆解成最基本的基因单位,并构建完整的生物体------也就是 AST!
二、类型检查:编译器的"法医鉴定"
在 TypeScript 编译器的 checker.ts 文件里,有一支法医团队,他们负责检查代码的 DNA 是否匹配。
typescript
*// 类型检查核心代码(简化版)*
function checkPropertyAccess(node: PropertyAccessExpression) {
const type = checkExpression(node.expression); *// 先检查对象的类型*
const propName = node.name.text; *// 获取属性名*
if (!type.getProperty(propName)) { *// 开始比对 DNA*
error(
node.name,
`类型'${typeToString(type)}'上不存在属性'${propName}'`
); *// 发现 DNA 不匹配,抛出错误报告*
}
}
血腥案例
const 死因 = "他杀";
console.log(死因.toFixed(2)); // 凶器:字符串的 toFixed 方法
编译器的调查报告:
-
鉴定 死因 的类型是 string
-
查阅 string 的 DNA 记录,发现它没有 toFixed 方法
-
立即拉响警报:"非法调用,DNA 不匹配!"
类型推断的 X 光片
typescript
*// 类型推断机制*
function inferTypeFromExpression(node: Expression) {
if (node.kind === SyntaxKind.NumericLiteral) {
return numberType; *// 数字字面量 -> number*
}
if (node.kind === SyntaxKind.StringLiteral) {
return stringType; *// 字符串字面量 -> string*
}
*// ...其他推断逻辑*
}
这样,每个变量的类型都被精准地标记在 DNA 序列上。
三、AST 操纵:代码整容手术
有时候,我们需要修改代码结构,比如自动添加类型注解,这就像给代码做整容手术。
typescript
import ts from 'typescript';
*// 原始代码*
const ast = ts.createSourceFile('demo.ts', 'let 年龄 = "二十五";', ts.ScriptTarget.Latest);
const varStatement = ast.statements[0] as ts.VariableStatement;
const varDecl = varStatement.declarationList.declarations[0];
*// 给变量添加类型注解*
varDecl.type = ts.factory.createTypeReferenceNode("number");
*// 打印修改后的代码*
const printer = ts.createPrinter();
console.log(printer.printFile(ast));
**效果**:
let 年龄: number = "二十五"; *// 这显然是不对的!*
编译器当场发飙:
类型"string"的值不能赋给类型"number"的变量
就像给熊猫染色,编译器这个"动物保护组织"立刻抗议。
四、类型体操的解剖实验室
泛型类型推导是 TypeScript 里最精密的 DNA 重组技术。例如:
type 量子纠缠 = T extends { 态: infer U } ? U : never;
当我们传入:
type 薛定谔的猫 = 量子纠缠<{ 态: "既死又活" }>;
TypeScript 内部的 DNA 鉴定过程:
-
发现 T 里包含 { 态: "既死又活" }
-
提取 态 的 DNA 片段,得到 "既死又活"
-
最终推导出 type 薛定谔的猫 = "既死又活"
五、编译器的"黑匣子"解密
调试秘籍
运行 tsc --showConfig,你会看到编译器的手术方案:
typescript
{
"compilerOptions": {
"strict": true, *// 开启严格模式*
"noImplicitAny": true, *// 禁止隐式 any*
"target": "ES2022" *// 目标环境设定*
}
}
如果想加速编译,可以启用:
typescript
{
"incremental": true *// 增量编译,加快二次编译速度*
}
后记
下次你的 IDE 报错时,不妨想象 TypeScript 编译器里有个穿白大褂的怪博士,拿着 DNA 采样器,对着你的代码说:
"年轻人,你这个变量整容过吧?身份证和脸对不上啊!"