Babel TypeScript 类型完全参考
本文档专门面向 TypeScript 使用者,覆盖 Babel 工具链所有包的完整类型定义,重点解决"对象很多、没有类型提示很费心神"的问题。 配合 技巧步骤/Babel AST入门 阅读,后者侧重基本用法和实战示例,本文侧重 TypeScript 类型系统。 1. 安装与 TypeScript 配置(#%E5%AE%89%E8%A3%85%E4%B8%8E-typescript-%E9%85%8D%E7%BD%AE) 2. @babel/parser 类型详解(#babelparser-%E7%B1%BB%E5%9E%8B%E8%AF%A6%E8%A7
官方文档:https://babeljs.io/docs/babel-types
适用版本:@babel/types 7.x+(2026-05-08 核实)
本文档专门面向 TypeScript 使用者,覆盖 Babel 工具链所有包的完整类型定义,重点解决"对象很多、没有类型提示很费心神"的问题。
配合 技巧步骤/Babel AST入门 阅读,后者侧重基本用法和实战示例,本文侧重 TypeScript 类型系统。
目录
- 安装与 TypeScript 配置
- @babel/parser 类型详解
- @babel/types 节点类型详解
- @babel/traverse 类型详解
- @babel/generator 类型详解
- @babel/core 类型详解
- TypeScript 使用模式
安装与 TypeScript 配置
安装命令
# 运行时依赖
npm install @babel/parser @babel/traverse @babel/types @babel/generator @babel/core
# TypeScript 类型声明
# @babel/parser、@babel/types、@babel/core 自带类型,无需单独安装
npm install --save-dev @types/babel__traverse @types/babel__generator
各包类型来源汇总
| 包 | 类型声明位置 | 说明 |
|---|---|---|
@babel/parser |
包内自带 | node_modules/@babel/parser/typings/babel-parser.d.ts |
@babel/types |
包内自带 | 自动生成,最完整 |
@babel/core |
包内自带 | node_modules/@babel/core/lib/index.d.ts |
@babel/traverse |
DefinitelyTyped | @types/babel__traverse |
@babel/generator |
DefinitelyTyped | @types/babel__generator |
tsconfig.json 推荐配置
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"skipLibCheck": false
}
}
esModuleInterop: true 是关键,因为 @babel/traverse 和 @babel/generator 用 export default 导出,但实际是 CommonJS 模块,没有这个选项 import traverse from 会报错。
推荐的 import 方式
// @babel/parser —— 具名导入
import { parse, parseExpression } from "@babel/parser";
// @babel/types —— 命名空间导入(对象很多,用 * as t 最方便)
import * as t from "@babel/types";
// @babel/traverse —— 默认导入(需要 esModuleInterop)
import traverse from "@babel/traverse";
// @babel/generator —— 默认导入
import generate from "@babel/generator";
// @babel/core —— 具名导入
import { transformSync, parseSync } from "@babel/core";
@babel/parser 类型详解
函数签名
import { parse, parseExpression } from "@babel/parser";
// 解析完整程序,返回 File 节点(根节点)
function parse(code: string, options?: ParserOptions): t.File;
// 解析单个表达式,返回 Expression 节点
function parseExpression(code: string, options?: ParserOptions): t.Expression;
ParserOptions 接口(完整字段)
interface ParserOptions {
// ---- 语法模式 ----
// 模块类型
// "script" : 默认,普通脚本,不支持 import/export
// "module" : ES 模块,支持 import/export
// "commonjs" : CommonJS(允许顶层 return、new.target、资源声明)
// "unambiguous" : 自动检测(有 import/export 则为 module,否则为 script)
sourceType?: "script" | "commonjs" | "module" | "unambiguous";
// 强制严格模式(默认 false)
strictMode?: boolean;
// 启用语法插件(见下方插件列表)
plugins?: ParserPlugin[];
// ---- 容错与恢复 ----
// 遇到语法错误时继续解析,而非抛出异常(处理混淆代码时建议开启)
errorRecovery?: boolean;
// ---- 位置信息 ----
// 源文件名,会附加到位置信息中
sourceFilename?: string;
// 起始行号(默认 1)
startLine?: number;
// 起始列号(默认 0)
startColumn?: number;
// 起始字符索引(默认 0)
startIndex?: number;
// 每个节点附加 [start, end] 位置数组(默认 false)
ranges?: boolean;
// ---- 节点控制 ----
// 把 token 列表附加到 AST 的 tokens 字段(默认 false)
tokens?: boolean;
// 是否将注释挂载到相邻节点(默认 true)
attachComment?: boolean;
// 把 (expr) 括号表达式包装成 ParenthesizedExpression 节点(默认 false)
createParenthesizedExpressions?: boolean;
// 把 import() 解析为 ImportExpression 而非 CallExpression(默认 true)
createImportExpressions?: boolean;
// ---- 特殊权限 ----
// 允许在任意位置使用 import/export(默认 false)
allowImportExportEverywhere?: boolean;
// 允许在函数外使用 await(默认 false)
allowAwaitOutsideFunction?: boolean;
// 允许在函数外使用 yield(默认 false)
allowYieldOutsideFunction?: boolean;
// 允许在函数外使用 new.target(默认 false)
allowNewTargetOutsideFunction?: boolean;
// 允许在函数外使用 return(默认 false)
allowReturnOutsideFunction?: boolean;
// 允许在非方法中使用 super(默认 false)
allowSuperOutsideMethod?: boolean;
// 允许未声明的 export(默认 false)
allowUndeclaredExports?: boolean;
// 是否遵循 Annex B(传统 JS 兼容性规则,默认 true)
annexB?: boolean;
}
plugins 可选值
plugins 字段接受 ParserPlugin[],每个元素可以是字符串,也可以是 [插件名, 选项对象] 的元组。
type ParserPlugin =
// 语言扩展
| "flow"
| "flowComments"
| "jsx"
| "typescript"
| "v8intrinsic"
// 实验性 ECMAScript 提案
| "asyncDoExpressions"
| "decimal"
| "decorators" // 需要配合选项:["decorators", { version: "2023-11", decoratorsBeforeExport: true }]
| "decorators-legacy"
| "decoratorAutoAccessors"
| "deferredImportEvaluation"
| "deprecatedImportAssert"
| "destructuringPrivate"
| "discardBinding"
| "doExpressions"
| "explicitResourceManagement" // using / await using
| "exportDefaultFrom"
| "functionBind"
| "functionSent"
| "importReflection"
| "moduleBlocks"
| "optionalChainingAssign"
| "partialApplication"
| "pipelineOperator" // 需要配合选项:["pipelineOperator", { proposal: "minimal" }]
| "recordAndTuple"
| "sourcePhaseImports"
| "throwExpressions"
// ESTree 兼容输出(供 ESLint 使用)
| "estree"
// 以下默认已启用
| "asyncGenerators"
| "bigInt"
| "classProperties"
| "classPrivateProperties"
| "classPrivateMethods"
| "classStaticBlock"
| "dynamicImport"
| "exportNamespaceFrom"
| "logicalAssignment"
| "nullishCoalescingOperator"
| "numericSeparator"
| "objectRestSpread"
| "optionalCatchBinding"
| "optionalChaining"
| "privateIn"
| "regexpUnicodeSets"
| "topLevelAwait"
| "importAttributes";
带类型的常用示例
import { parse, parseExpression } from "@babel/parser";
import * as t from "@babel/types";
// 返回类型明确为 t.File
const ast: t.File = parse(`const x = 1`, {
sourceType: "module",
plugins: ["typescript"],
errorRecovery: true,
});
// Program 是 ast.program,包含所有顶层语句
const program: t.Program = ast.program;
// parseExpression 返回 t.Expression(联合类型,需要收窄)
const expr: t.Expression = parseExpression(`a + b`);
if (t.isBinaryExpression(expr)) {
// 这里 expr 被收窄为 t.BinaryExpression
console.log(expr.operator); // string,类型安全
}
@babel/types 节点类型详解
BaseNode 基础接口
所有 AST 节点都继承 BaseNode:
interface BaseNode {
type: string;
start?: number | null; // 起始字符偏移
end?: number | null; // 结束字符偏移
loc?: SourceLocation | null; // 行列位置信息
leadingComments?: Comment[];
innerComments?: Comment[];
trailingComments?: Comment[];
extra?: Record<string, unknown> | null;
}
interface SourceLocation {
start: Position;
end: Position;
filename: string;
identifierName?: string | null;
}
interface Position {
line: number; // 1-based
column: number; // 0-based
index: number; // 字符偏移,0-based
}
核心节点 TypeScript 接口定义
顶层节点
interface File extends BaseNode {
type: "File";
program: Program;
comments?: (CommentBlock | CommentLine)[] | null;
tokens?: any[] | null;
}
interface Program extends BaseNode {
type: "Program";
body: Statement[];
directives: Directive[]; // "use strict" 等指令
sourceType: "script" | "module";
interpreter?: InterpreterDirective | null; // #!/usr/bin/env node
}
标识符与字面量
interface Identifier extends BaseNode {
type: "Identifier";
name: string;
// 以下字段在 TypeScript/Flow 语法中出现
optional?: boolean | null;
typeAnnotation?: TypeAnnotation | TSTypeAnnotation | null;
decorators?: Decorator[] | null;
}
interface StringLiteral extends BaseNode {
type: "StringLiteral";
value: string;
}
interface NumericLiteral extends BaseNode {
type: "NumericLiteral";
value: number;
}
interface BooleanLiteral extends BaseNode {
type: "BooleanLiteral";
value: boolean;
}
interface NullLiteral extends BaseNode {
type: "NullLiteral";
}
interface BigIntLiteral extends BaseNode {
type: "BigIntLiteral";
value: string; // 注意:是字符串,不是 bigint
}
interface RegExpLiteral extends BaseNode {
type: "RegExpLiteral";
pattern: string;
flags: string;
}
// 模板字符串:`hello ${name}`
interface TemplateLiteral extends BaseNode {
type: "TemplateLiteral";
quasis: TemplateElement[]; // 静态文本片段(n+1 个)
expressions: (Expression | TSType)[]; // 动态插值(n 个)
}
// 模板字符串的静态片段
interface TemplateElement extends BaseNode {
type: "TemplateElement";
value: {
raw: string; // 原始文本(含转义符,如 \n)
cooked: string | null; // 解析后文本(null 表示含非法转义)
};
tail: boolean; // 是否是最后一个片段
}
表达式节点
// obj.prop(computed=false)或 obj[expr](computed=true)
// 注意:MemberExpression 是联合类型
type MemberExpression = MemberExpressionComputed | MemberExpressionNonComputed;
interface MemberExpressionNonComputed extends BaseNode {
type: "MemberExpression";
object: Expression | Super;
property: Identifier | PrivateName;
computed: false;
optional?: boolean | null;
}
interface MemberExpressionComputed extends BaseNode {
type: "MemberExpression";
object: Expression | Super;
property: Expression;
computed: true;
optional?: boolean | null;
}
// fn(a, b)
interface CallExpression extends BaseNode {
type: "CallExpression";
callee: Expression | Super | V8IntrinsicIdentifier;
arguments: (Expression | SpreadElement | ArgumentPlaceholder)[];
optional?: boolean | null;
typeArguments?: TypeParameterInstantiation | TSTypeParameterInstantiation | null;
}
// a + b, a === b, a in b 等
interface BinaryExpression extends BaseNode {
type: "BinaryExpression";
operator:
| "+" | "-" | "/" | "%" | "*" | "**"
| "&" | "|" | ">>" | ">>>" | "<<" | "^"
| "==" | "===" | "!=" | "!=="
| "in" | "instanceof"
| ">" | "<" | ">=" | "<="
| "|>";
left: Expression | PrivateName;
right: Expression;
}
// x = 1, a += b 等
interface AssignmentExpression extends BaseNode {
type: "AssignmentExpression";
operator:
| "=" | "+=" | "-=" | "/=" | "%=" | "*=" | "**="
| "&=" | "|=" | ">>=" | ">>>=" | "<<=" | "^="
| "||=" | "&&=" | "??=";
left:
| Identifier | MemberExpression | OptionalMemberExpression
| ArrayPattern | ObjectPattern
| TSAsExpression | TSSatisfiesExpression | TSTypeAssertion | TSNonNullExpression;
right: Expression;
}
// { a: 1, b: 2 }
interface ObjectExpression extends BaseNode {
type: "ObjectExpression";
properties: (ObjectMethod | ObjectProperty | SpreadElement)[];
}
// 对象属性(也是联合类型)
type ObjectProperty = ObjectPropertyComputed | ObjectPropertyNonComputed;
interface ObjectPropertyNonComputed extends BaseNode {
type: "ObjectProperty";
key: Identifier | StringLiteral | NumericLiteral | BigIntLiteral | PrivateName;
value: Expression | PatternLike;
computed: false;
shorthand: boolean; // 是否是简写形式:{ a } 而非 { a: a }
decorators?: Decorator[] | null;
}
interface ObjectPropertyComputed extends BaseNode {
type: "ObjectProperty";
key: Expression;
value: Expression | PatternLike;
computed: true;
shorthand: boolean;
decorators?: Decorator[] | null;
}
// [1, 2, 3]
interface ArrayExpression extends BaseNode {
type: "ArrayExpression";
elements: (null | Expression | SpreadElement)[]; // null 表示空位:[1,,3]
}
函数节点
// function foo(a, b) { ... }
interface FunctionDeclaration extends BaseNode {
type: "FunctionDeclaration";
id: Identifier | null; // 匿名函数声明时为 null(export default function)
params: FunctionParameter[];
body: BlockStatement;
generator: boolean; // function*
async: boolean; // async function
declare?: boolean | null; // TypeScript: declare function
returnType?: TypeAnnotation | TSTypeAnnotation | null;
typeParameters?: TypeParameterDeclaration | TSTypeParameterDeclaration | null;
predicate?: FlowPredicate | null;
}
// const fn = function(a, b) { ... }
interface FunctionExpression extends BaseNode {
type: "FunctionExpression";
id?: Identifier | null; // 具名函数表达式:const fn = function foo() {}
params: FunctionParameter[];
body: BlockStatement;
generator: boolean;
async: boolean;
returnType?: TypeAnnotation | TSTypeAnnotation | null;
typeParameters?: TypeParameterDeclaration | TSTypeParameterDeclaration | null;
predicate?: FlowPredicate | null;
}
// (a, b) => a + b 或 (a) => { return a }
interface ArrowFunctionExpression extends BaseNode {
type: "ArrowFunctionExpression";
params: FunctionParameter[];
body: BlockStatement | Expression; // 表达式体时为 Expression,有花括号时为 BlockStatement
async: boolean;
expression: boolean; // body 是 Expression(非 BlockStatement)时为 true
generator?: boolean | null; // 箭头函数不能是 generator,此字段为 null/false
returnType?: TypeAnnotation | TSTypeAnnotation | null;
typeParameters?: TypeParameterDeclaration | TSTypeParameterDeclaration | null;
predicate?: FlowPredicate | null;
}
// FunctionParameter 是参数类型的联合
type FunctionParameter =
| Identifier
| Pattern // 解构参数
| RestElement // ...rest
| TSParameterProperty; // TypeScript: public/private/readonly 参数
声明节点
// var/let/const/using/await using x = 1
interface VariableDeclaration extends BaseNode {
type: "VariableDeclaration";
kind: "var" | "let" | "const" | "using" | "await using";
declarations: VariableDeclarator[];
declare?: boolean | null; // TypeScript: declare const x: string
}
// x = 1(VariableDeclaration 中的单个声明项)
interface VariableDeclarator extends BaseNode {
type: "VariableDeclarator";
id: Identifier | ArrayPattern | ObjectPattern | VoidPattern;
init?: Expression | null; // null 表示 let x 没有初始值
definite?: boolean | null; // TypeScript: let x!: string
}
语句节点
// { ... }
interface BlockStatement extends BaseNode {
type: "BlockStatement";
body: Statement[];
directives: Directive[]; // "use strict" 等
}
// 将表达式作为语句:foo()、a = 1 等
interface ExpressionStatement extends BaseNode {
type: "ExpressionStatement";
expression: Expression;
}
// return x
interface ReturnStatement extends BaseNode {
type: "ReturnStatement";
argument?: Expression | null; // return; 时为 null
}
// if (test) consequent else alternate
interface IfStatement extends BaseNode {
type: "IfStatement";
test: Expression;
consequent: Statement;
alternate?: Statement | null; // 无 else 时为 null
}
// throw err
interface ThrowStatement extends BaseNode {
type: "ThrowStatement";
argument: Expression;
}
// try { ... } catch (e) { ... } finally { ... }
interface TryStatement extends BaseNode {
type: "TryStatement";
block: BlockStatement;
handler?: CatchClause | null;
finalizer?: BlockStatement | null;
}
// while (test) body
interface WhileStatement extends BaseNode {
type: "WhileStatement";
test: Expression;
body: Statement;
}
// for (init; test; update) body
interface ForStatement extends BaseNode {
type: "ForStatement";
init?: VariableDeclaration | Expression | null;
test?: Expression | null;
update?: Expression | null;
body: Statement;
}
模块节点
// import { a, b as c } from 'mod'
// import DefaultExport from 'mod'
// import * as ns from 'mod'
interface ImportDeclaration extends BaseNode {
type: "ImportDeclaration";
specifiers: (ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier)[];
source: StringLiteral;
attributes?: ImportAttribute[] | null; // import ... with { type: "json" }
importKind?: "type" | "typeof" | "value" | null; // Flow/TypeScript: import type
phase?: "source" | "defer" | null; // import.source / import.defer 提案
module?: boolean | null;
}
// { a } 或 { a as b }
interface ImportSpecifier extends BaseNode {
type: "ImportSpecifier";
local: Identifier;
imported: Identifier | StringLiteral;
importKind?: "type" | "typeof" | "value" | null;
}
// DefaultExport(import 中的默认导入)
interface ImportDefaultSpecifier extends BaseNode {
type: "ImportDefaultSpecifier";
local: Identifier;
}
// * as ns
interface ImportNamespaceSpecifier extends BaseNode {
type: "ImportNamespaceSpecifier";
local: Identifier;
}
// export { a, b as c }
// export const x = 1
// export { a } from 'mod'
interface ExportNamedDeclaration extends BaseNode {
type: "ExportNamedDeclaration";
declaration?: Declaration | null; // export const x = 1 时有值
specifiers: (ExportSpecifier | ExportDefaultSpecifier | ExportNamespaceSpecifier)[];
source?: StringLiteral | null; // export ... from 'mod' 时有值
attributes?: ImportAttribute[] | null;
exportKind?: "type" | "value" | null; // export type { Foo }
}
// export default foo
interface ExportDefaultDeclaration extends BaseNode {
type: "ExportDefaultDeclaration";
declaration: Declaration | Expression;
exportKind?: "value" | null;
}
// export * from 'mod'
interface ExportAllDeclaration extends BaseNode {
type: "ExportAllDeclaration";
source: StringLiteral;
exported?: Identifier | StringLiteral | null; // export * as ns from 'mod'
attributes?: ImportAttribute[] | null;
exportKind?: "type" | "value" | null;
}
节点别名(类型联合)
@babel/types 定义了大量别名,在 visitor 中可以用别名一次匹配多种节点:
// 主要别名(可直接在 traverse visitor 中使用)
type Expression =
| Identifier | StringLiteral | NumericLiteral | BooleanLiteral | NullLiteral
| TemplateLiteral | MemberExpression | CallExpression | NewExpression
| BinaryExpression | UnaryExpression | LogicalExpression | AssignmentExpression
| ConditionalExpression | SequenceExpression | ArrowFunctionExpression
| FunctionExpression | ObjectExpression | ArrayExpression
| TaggedTemplateExpression | YieldExpression | AwaitExpression
| /* ... 更多 */;
type Statement =
| ExpressionStatement | BlockStatement | ReturnStatement | IfStatement
| VariableDeclaration | FunctionDeclaration | ClassDeclaration
| ForStatement | ForInStatement | ForOfStatement | WhileStatement
| DoWhileStatement | SwitchStatement | TryStatement | ThrowStatement
| BreakStatement | ContinueStatement | LabeledStatement
| ImportDeclaration | ExportNamedDeclaration | ExportDefaultDeclaration
| ExportAllDeclaration | /* ... 更多 */;
type Declaration =
| FunctionDeclaration | ClassDeclaration | VariableDeclaration
| ExportNamedDeclaration | ExportDefaultDeclaration | ExportAllDeclaration
| ImportDeclaration | /* ... 更多 */;
type Pattern =
| AssignmentPattern | ArrayPattern | ObjectPattern | RestElement;
type Node =
| Expression | Statement | Declaration | Pattern
| File | Program | TemplateElement | SpreadElement | ObjectProperty
| ObjectMethod | ClassBody | ClassMethod | ClassProperty
| /* ... 所有节点的联合类型 */;
类型守卫函数(isXxx)
每个节点类型都有对应的类型守卫函数,调用后 TypeScript 会自动收窄类型:
import * as t from "@babel/types";
// 基本签名:
// t.isXxx(node: object | null | undefined, opts?: object): node is Xxx
// opts 是可选的属性过滤器,所有字段都要匹配才返回 true
t.isIdentifier(node) // node is t.Identifier
t.isIdentifier(node, { name: "foo" }) // name === "foo" 时才匹配
t.isStringLiteral(node) // node is t.StringLiteral
t.isNumericLiteral(node)
t.isBooleanLiteral(node)
t.isNullLiteral(node)
t.isTemplateLiteral(node)
t.isMemberExpression(node)
t.isCallExpression(node)
t.isBinaryExpression(node)
t.isBinaryExpression(node, { operator: "+" }) // 只匹配 + 运算
t.isAssignmentExpression(node)
t.isObjectExpression(node)
t.isObjectProperty(node)
t.isArrayExpression(node)
t.isFunctionDeclaration(node)
t.isFunctionExpression(node)
t.isArrowFunctionExpression(node)
t.isVariableDeclaration(node)
t.isVariableDeclarator(node)
t.isBlockStatement(node)
t.isExpressionStatement(node)
t.isReturnStatement(node)
t.isIfStatement(node)
t.isImportDeclaration(node)
t.isExportNamedDeclaration(node)
// 别名守卫(匹配整个类别)
t.isExpression(node) // node is t.Expression
t.isStatement(node) // node is t.Statement
t.isDeclaration(node) // node is t.Declaration
t.isLiteral(node) // 匹配所有字面量类型
t.isScope(node) // 有作用域的节点
t.isFunction(node) // 所有函数形式
t.isClass(node) // 所有 class 形式
断言函数(assertXxx)
assertXxx 与 isXxx 的区别:不匹配时抛出 TypeError 而非返回 false。
适合在你确定类型正确但 TypeScript 无法推断的场合做强制收窄。
// 基本签名:
// t.assertXxx(node: object | null | undefined, opts?: object): asserts node is Xxx
t.assertIdentifier(node); // 断言后 node 类型为 t.Identifier
t.assertCallExpression(node);
t.assertFunctionDeclaration(node);
// 实际使用示例
function processCallee(node: t.Node) {
t.assertMemberExpression(node);
// 这里 node 已被收窄为 t.MemberExpression
console.log(node.object, node.property);
}
构建器函数(完整签名)
import * as t from "@babel/types";
// ---- 字面量 ----
t.identifier(name: string): t.Identifier
t.stringLiteral(value: string): t.StringLiteral
t.numericLiteral(value: number): t.NumericLiteral
t.booleanLiteral(value: boolean): t.BooleanLiteral
t.nullLiteral(): t.NullLiteral
t.bigIntLiteral(value: string): t.BigIntLiteral
t.regExpLiteral(pattern: string, flags?: string): t.RegExpLiteral
// templateLiteral 的 quasis 比 expressions 多一个
t.templateLiteral(
quasis: t.TemplateElement[],
expressions: (t.Expression | t.TSType)[]
): t.TemplateLiteral
t.templateElement(
value: { raw: string; cooked?: string | null },
tail?: boolean // 默认 false,最后一个片段需要传 true
): t.TemplateElement
// ---- 表达式 ----
t.memberExpression(
object: t.Expression | t.Super,
property: t.Expression | t.Identifier | t.PrivateName,
computed?: boolean, // 默认 false
optional?: boolean | null
): t.MemberExpression
t.callExpression(
callee: t.Expression | t.Super | t.V8IntrinsicIdentifier,
args: (t.Expression | t.SpreadElement | t.ArgumentPlaceholder)[],
typeArguments?: t.TypeParameterInstantiation | t.TSTypeParameterInstantiation | null
): t.CallExpression
t.binaryExpression(
operator: "+" | "-" | "/" | "%" | "*" | "**" | "&" | "|" | ">>" | ">>>"
| "<<" | "^" | "==" | "===" | "!=" | "!==" | "in" | "instanceof"
| ">" | "<" | ">=" | "<=" | "|>",
left: t.Expression | t.PrivateName,
right: t.Expression
): t.BinaryExpression
t.assignmentExpression(
operator: "=" | "+=" | "-=" | "/=" | "%=" | "*=" | "**="
| "&=" | "|=" | ">>=" | ">>>=" | "<<=" | "^="
| "||=" | "&&=" | "??=",
left: t.LVal,
right: t.Expression
): t.AssignmentExpression
t.objectExpression(
properties: (t.ObjectMethod | t.ObjectProperty | t.SpreadElement)[]
): t.ObjectExpression
t.objectProperty(
key: t.Expression | t.Identifier | t.StringLiteral | t.NumericLiteral | t.PrivateName,
value: t.Expression | t.PatternLike,
computed?: boolean, // 默认 false
shorthand?: boolean, // 默认 false
decorators?: t.Decorator[] | null
): t.ObjectProperty
t.objectMethod(
kind: "method" | "get" | "set",
key: t.Expression | t.Identifier | t.StringLiteral | t.NumericLiteral | t.PrivateName,
params: t.FunctionParameter[],
body: t.BlockStatement,
computed?: boolean,
generator?: boolean,
async?: boolean
): t.ObjectMethod
t.arrayExpression(
elements?: (null | t.Expression | t.SpreadElement)[]
): t.ArrayExpression
t.spreadElement(argument: t.Expression): t.SpreadElement
// ---- 函数 ----
t.functionDeclaration(
id: t.Identifier | null,
params: t.FunctionParameter[],
body: t.BlockStatement,
generator?: boolean, // 默认 false
async?: boolean // 默认 false
): t.FunctionDeclaration
t.functionExpression(
id: t.Identifier | null | undefined,
params: t.FunctionParameter[],
body: t.BlockStatement,
generator?: boolean,
async?: boolean
): t.FunctionExpression
t.arrowFunctionExpression(
params: t.FunctionParameter[],
body: t.BlockStatement | t.Expression,
async?: boolean // 默认 false
): t.ArrowFunctionExpression
// ---- 语句 ----
t.blockStatement(
body: t.Statement[],
directives?: t.Directive[]
): t.BlockStatement
t.expressionStatement(expression: t.Expression): t.ExpressionStatement
t.returnStatement(argument?: t.Expression | null): t.ReturnStatement
t.ifStatement(
test: t.Expression,
consequent: t.Statement,
alternate?: t.Statement | null
): t.IfStatement
t.throwStatement(argument: t.Expression): t.ThrowStatement
t.tryStatement(
block: t.BlockStatement,
handler?: t.CatchClause | null,
finalizer?: t.BlockStatement | null
): t.TryStatement
// ---- 声明 ----
t.variableDeclaration(
kind: "var" | "let" | "const" | "using" | "await using",
declarations: t.VariableDeclarator[]
): t.VariableDeclaration
t.variableDeclarator(
id: t.LVal,
init?: t.Expression | null
): t.VariableDeclarator
// ---- 模块 ----
t.importDeclaration(
specifiers: (t.ImportSpecifier | t.ImportDefaultSpecifier | t.ImportNamespaceSpecifier)[],
source: t.StringLiteral,
attributes?: t.ImportAttribute[] | null
): t.ImportDeclaration
t.importSpecifier(
local: t.Identifier,
imported: t.Identifier | t.StringLiteral,
importKind?: "type" | "typeof" | "value" | null
): t.ImportSpecifier
t.importDefaultSpecifier(local: t.Identifier): t.ImportDefaultSpecifier
t.importNamespaceSpecifier(local: t.Identifier): t.ImportNamespaceSpecifier
t.exportNamedDeclaration(
declaration?: t.Declaration | null,
specifiers?: (t.ExportSpecifier | t.ExportDefaultSpecifier | t.ExportNamespaceSpecifier)[],
source?: t.StringLiteral | null,
attributes?: t.ImportAttribute[] | null
): t.ExportNamedDeclaration
t.exportSpecifier(
local: t.Identifier,
exported: t.Identifier | t.StringLiteral
): t.ExportSpecifier
t.exportDefaultDeclaration(
declaration: t.Declaration | t.Expression
): t.ExportDefaultDeclaration
节点工具函数
// 深度克隆节点(包含所有子节点)
// withoutLocations: true 时去掉位置信息(默认 false)
t.cloneNode<T extends t.Node>(node: T, deep?: boolean, withoutLocations?: boolean): T
// 浅克隆(只克隆顶层,子节点共享引用)
t.shallowEqual(actual: object, expected: object): boolean
// 递归清除节点的位置信息、注释等元数据
// opts.preserveComments: 保留注释(默认 false)
t.removePropertiesDeep(node: t.Node, opts?: { preserveComments?: boolean }): t.Node
// 移除单个节点的位置/注释等非核心属性
t.removeProperties(node: t.Node, opts?: { preserveComments?: boolean }): void
// 判断节点是否表示纯值(无副作用)
t.isPureish(node: t.Node): boolean
// 获取节点的所有 binding 标识符
t.getBindingIdentifiers(node: t.Node, duplicates?: boolean, outerOnly?: boolean): { [name: string]: t.Identifier | t.Identifier[] }
// 判断两个节点是否相等(深度比较)
t.isNodesEquivalent(a: t.Node, b: t.Node): boolean
// 判断节点是否是引用(而非赋值目标)
t.isReferenced(node: t.Node, parent: t.Node, grandparent?: t.Node): boolean
// 获取节点的 type 字段,考虑别名
t.toComputedKey(node: t.ObjectMethod | t.ObjectProperty | t.ClassMethod | t.ClassProperty): t.Expression
@babel/traverse 类型详解
traverse() 函数签名
import traverse from "@babel/traverse";
import * as t from "@babel/types";
// 无 state 时(最常用形式)
function traverse(
parent: t.Node,
opts?: TraverseOptions,
scope?: Scope,
state?: any,
parentPath?: NodePath
): void;
// 有 state 时(泛型形式,state 类型安全)
function traverse<S>(
parent: t.Node,
opts: TraverseOptions<S>,
scope: Scope | undefined,
state: S,
parentPath?: NodePath
): void;
TraverseOptions / Visitor 接口
// Visitor 是 TraverseOptions 的核心
// 键是节点类型字符串,值是访问函数或 { enter, exit } 对象
type Visitor<S = unknown> =
// 每个节点类型名都可以作为键
& { [N in t.Node as N["type"]]?: VisitNode<S, N> }
// 别名也可以作为键(Function、Expression、Statement 等)
& { [K in keyof t.Aliases]?: VisitNode<S, t.Aliases[K]> }
// 多类型用 | 合并:'StringLiteral|NumericLiteral'
& { [k: `${string}|${string}`]: VisitNode<S, t.Node> }
// 入口/出口处理
& VisitNodeObject<S, t.Node>;
// 访问节点可以是函数或对象
type VisitNode<S, P extends t.Node> =
| VisitNodeFunction<S, P>
| VisitNodeObject<S, P>;
// 函数形式
type VisitNodeFunction<S, P extends t.Node> =
(this: S, path: NodePath<P>, state: S) => void;
// 对象形式(enter/exit)
interface VisitNodeObject<S, P extends t.Node> {
enter?: VisitNodeFunction<S, P>;
exit?: VisitNodeFunction<S, P>;
}
NodePath<T> 接口(完整字段)
NodePath<T> 是对节点的包装,T 是具体的节点类型(默认为 t.Node)。
interface NodePath<T extends t.Node = t.Node> {
// ---- 核心属性 ----
node: T; // 当前 AST 节点
type: T["type"] | undefined; // 节点类型字符串
parent: t.Node; // 父节点(AST 节点,非 path)
parentPath: NodePath | null; // 父节点的 path(根节点时为 null)
scope: Scope; // 当前作用域
// ---- 位置信息 ----
key: string | number | null; // 本节点在父节点中的字段名(如 "body"、"left"、0)
listKey: string | null; // 本节点所在的数组字段名(如 "body"),非数组时为 null
inList: boolean; // 本节点是否在数组中
// ---- 容器信息 ----
container: t.Node | t.Node[] | null; // 包含本节点的容器(数组或父节点字段)
hub: Hub; // 全局上下文
// ---- 状态标记 ----
removed: boolean; // 是否已被 remove()
replaced: boolean; // 是否已被 replaceWith()
skipped: boolean; // 是否已被 skip()
// ---- 用户数据 ----
data: Record<string | symbol, unknown>; // 可自由附加数据(如标记"已处理")
state: any; // traverse 传入的 state
// ---- 辅助 ----
contexts: TraversalContext[];
}
NodePath 上的方法
类型判断方法
// path.isXxx(opts?) 的形式对所有节点类型都有
// 返回 boolean,同时收窄 path 的泛型类型
path.isIdentifier(opts?: { name?: string }): this is NodePath<t.Identifier>
path.isStringLiteral(opts?: { value?: string }): this is NodePath<t.StringLiteral>
path.isCallExpression(): this is NodePath<t.CallExpression>
path.isMemberExpression(): this is NodePath<t.MemberExpression>
path.isFunctionDeclaration(): this is NodePath<t.FunctionDeclaration>
path.isExpression(): this is NodePath<t.Expression>
path.isStatement(): this is NodePath<t.Statement>
// ... 200+ 个 isXxx 方法,与 t.isXxx 一一对应
替换节点
// 用单个节点替换当前节点
path.replaceWith(node: t.Node | NodePath): void
// 用多个节点替换(当前节点必须在语句列表中)
path.replaceWithMultiple(nodes: t.Node[]): NodePath[]
// 用代码字符串替换(内部会解析代码)
path.replaceWithSourceString(replacement: string): void
// 将表达式替换为语句序列,返回包裹函数的调用表达式
path.replaceExpressionWithStatements(nodes: t.Statement[]): t.Node
插入节点
// 在当前节点之前插入(当前节点必须在语句列表中)
path.insertBefore(nodes: t.Node | t.Node[]): NodePath[]
// 在当前节点之后插入
path.insertAfter(nodes: t.Node | t.Node[]): NodePath[]
删除节点
path.remove(): void
控制遍历
// 跳过当前节点的所有子节点(本节点仍处理,但不再向下遍历)
path.skip(): void
// 立即停止整个 traverse 遍历
path.stop(): void
// 对当前节点的子节点发起遍历(手动递归)
path.traverse(opts: TraverseOptions, state?: any): void
查找父节点
// 向上查找满足条件的祖先 path(包含当前节点)
// 回调返回 true 时停止,返回该 path
path.find(callback: (path: NodePath) => boolean): NodePath | null
// 向上查找满足条件的祖先 path(不包含当前节点)
path.findParent(callback: (path: NodePath) => boolean): NodePath | null
// 向上找最近的函数作用域的 path
path.getFunctionParent(): NodePath<t.Function> | null
// 向上找最近的语句 path
path.getStatementParent(): NodePath<t.Statement> | null
// 获取从根到当前节点的所有祖先 path(不含当前)
path.getAncestry(): NodePath[]
// 判断某个 path 是否是当前 path 的祖先
path.isAncestor(maybeDescendant: NodePath): boolean
// 判断某个 path 是否是当前 path 的后代
path.isDescendant(maybeAncestor: NodePath): boolean
获取子路径
// 获取某个字段的子 path
// key 是字段名,listKey 是数组下标
path.get(key: string): NodePath | NodePath[]
path.get(key: string, context?: boolean): NodePath<t.Node> | NodePath<t.Node>[]
// 示例
const bodyPaths = path.get("body") as NodePath[];
const firstArg = path.get("arguments.0") as NodePath;
求值
// 静态求值:尝试计算节点的常量值
// confident=true 时才可信,否则 value 无意义
path.evaluate(): { confident: boolean; value: any; deopt?: NodePath }
// 只返回布尔值的求值(用于条件判断)
path.evaluateTruthy(): boolean | undefined
模式匹配
// 判断 MemberExpression 是否匹配指定模式
// 如:path.matchesPattern("console.log")
path.matchesPattern(pattern: string, allowPartial?: boolean): boolean
Binding 相关
// 获取当前节点中所有 binding 的 Identifier 节点
path.getBindingIdentifiers(duplicates?: boolean): { [name: string]: t.Identifier | t.Identifier[] }
// 获取外层 binding 的 Identifier(解构时有区别)
path.getOuterBindingIdentifiers(duplicates?: boolean): { [name: string]: t.Identifier }
// 获取所有 binding 的 NodePath
path.getBindingIdentifierPaths(duplicates?: boolean, outerOnly?: boolean): { [name: string]: NodePath<t.Identifier> }
Scope 接口
interface Scope {
// ---- 基础属性 ----
uid: number; // 作用域唯一 ID
path: NodePath; // 产生此作用域的节点 path
block: t.Node; // 产生此作用域的节点(与 path.node 相同)
parent: Scope; // 父作用域
hub: Hub;
// ---- 绑定信息 ----
// 当前作用域内所有变量绑定(键为变量名)
bindings: { [name: string]: Binding };
// 当前作用域内所有变量引用(只有键,值为 true)
references: { [name: string]: true };
// ---- 查询方法 ----
// 获取变量绑定(向上查找父作用域)
getBinding(name: string): Binding | undefined;
// 获取仅在当前作用域的绑定(不查父作用域)
getOwnBinding(name: string): Binding | undefined;
// 判断变量是否在当前或父作用域中有绑定
hasBinding(name: string, opts?: { noGlobals?: boolean; noUids?: boolean }): boolean;
// 判断变量是否仅在当前作用域绑定
hasOwnBinding(name: string): boolean;
// 判断变量是否被引用(有使用者)
hasReference(name: string): boolean;
// ---- 重命名 ----
// 重命名变量(自动处理所有引用点,包括声明和使用)
rename(oldName: string, newName?: string, block?: t.Node): void;
// ---- 生成新标识符 ----
// 生成一个当前作用域内唯一的 uid(字符串)
generateUid(name?: string): string;
// 生成唯一 uid 并返回 Identifier 节点
generateUidIdentifier(name?: string): t.Identifier;
// 基于某个节点名称生成唯一 uid
generateUidIdentifierBasedOnNode(node: t.Node, defaultName?: string): t.Identifier;
// ---- 作用域操作 ----
// 在当前作用域发起遍历
traverse(node: t.Node, opts: TraverseOptions, state?: any): void;
// 将引用推送到根作用域(提升变量)
push(opts: {
id: t.LVal;
init?: t.Expression | null;
unique?: boolean;
kind?: "var" | "let" | "const";
}): void;
// 删除当前作用域的绑定信息(强制重新爬取)
removeBinding(name: string): void;
// 重新爬取当前作用域的绑定信息
crawl(): void;
// 判断标识符是否是静态的(可以安全求值)
isStatic(node: t.Node): boolean;
// 获取所有绑定(包括父作用域)
getAllBindings(): { [name: string]: Binding };
// 获取所有绑定名称(包括父作用域)
getAllBindingNames(): string[];
}
Binding 类
Binding 表示一个变量的绑定信息,通过 scope.getBinding(name) 或 scope.bindings[name] 获取:
type BindingKind =
| "var" // var 声明
| "let" // let 声明
| "const" // const 声明
| "module" // import 声明
| "hoisted" // function 声明(提升)
| "param" // 函数参数
| "local" // 函数表达式的 id(如 const fn = function foo() {} 中的 foo)
| "unknown"; // 无法确定
class Binding {
identifier: t.Identifier; // 绑定的 Identifier 节点
scope: Scope; // 绑定所在的作用域
path: NodePath; // 声明处的 path
kind: BindingKind;
referenced: boolean; // 是否被引用过
references: number; // 被引用次数
referencePaths: NodePath[]; // 所有引用处的 path
constant: boolean; // 是否是常量(没有赋值操作)
constantViolations: NodePath[]; // 所有对该变量赋值的 path
// 标记一次引用
reference(path: NodePath): void;
// 取消一次引用
dereference(): void;
// 标记一次赋值
reassign(path: NodePath): void;
}
@babel/generator 类型详解
generate() 函数签名
import generate from "@babel/generator";
import * as t from "@babel/types";
// ast 可以是任意 Node,不只是 File
// code 是原始代码(用于 source map)
// 如果是多文件场景,code 可以是文件名到代码内容的映射
function generate(
ast: t.Node,
opts?: GeneratorOptions,
code?: string | { [filename: string]: string }
): GeneratorResult;
GeneratorOptions 接口(所有字段)
interface GeneratorOptions {
// ---- 注释控制 ----
// 在注入代码之前添加的注释(如块级帮助函数)
auxiliaryCommentBefore?: string;
// 在注入代码之后添加的注释
auxiliaryCommentAfter?: string;
// 是否输出注释(默认 true)
comments?: boolean;
// 自定义注释过滤器(返回 true 时输出该注释)
shouldPrintComment?: (comment: string) => boolean;
// ---- 格式控制 ----
// 是否紧凑模式(去掉大部分空格)
// true: 强制紧凑;false: 强制非紧凑;"auto": 跟随 minified 选项
compact?: boolean | "auto";
// 是否适度减少空格(比 compact 弱,默认 false)
concise?: boolean;
// 是否完全压缩(同时开启 compact,默认 false)
minified?: boolean;
// 尝试保留原始行号(可能影响代码可读性,默认 false)
retainLines?: boolean;
// 保留函数调用的括号(防止某些压缩工具误删,默认 false)
retainFunctionParens?: boolean;
// ---- 字符串/特殊语法 ----
// 关联的文件名(影响 source map 的 file 字段)
filename?: string;
// 是否将字符串输出为 JSON 兼容格式(全部用 Unicode 转义,默认 false)
jsonCompatibleStrings?: boolean;
// jsesc 库的选项(控制字符串转义行为)
// 常用选项:{ minimal: true } 只转义必须转义的字符
jsescOption?: {
quotes?: "single" | "double" | "backtick";
minimal?: boolean;
numbers?: "decimal" | "hexadecimal" | "octal" | "binary";
indent?: string;
indentLevel?: number;
json?: boolean;
es6?: boolean;
escapeEverything?: boolean;
wrap?: boolean;
isScriptContext?: boolean;
compact?: boolean;
lowercaseHex?: boolean;
};
// ---- 特殊语法选项 ----
// decorator 放在 export 前还是后(默认 false 即放在后面)
decoratorsBeforeExport?: boolean;
// Record/Tuple 的语法类型:#[...] 和 #{ } 或 |[ ]| 和 |{ }|(默认 "hash")
recordAndTupleSyntaxType?: "hash" | "bar";
// |> 管道运算符的 topic token(默认 "%")
topicToken?: "%" | "#" | "@@" | "^^";
// import attributes 使用 with 还是 assert 关键字
importAttributesKeyword?: "with" | "assert" | "with-legacy";
// ---- Source Map ----
// 是否生成 source map(默认 false)
sourceMaps?: boolean;
// source map 的根路径
sourceRoot?: string;
// source map 中的文件名
sourceFileName?: string;
// 输入的 source map(用于 map 的 map)
inputSourceMap?: string | object;
// ---- 实验性 ----
// 实验性:尽量保留原始代码格式(需要 @babel/parser 的 tokens 选项)
experimental_preserveFormat?: boolean;
}
GeneratorResult 接口
interface GeneratorResult {
// 生成的代码字符串
code: string;
// source map 对象(sourceMaps=false 时为 null)
map: {
version: number;
sources: string[];
names: string[];
sourceRoot?: string;
sourcesContent?: string[];
mappings: string; // VLQ 编码的位置映射
file: string;
} | null;
// 解码后的 source map(内部使用,通常不需要)
decodedMap?: DecodedSourceMap;
// 原始映射数组(内部使用)
rawMappings?: Mapping[];
}
@babel/core 类型详解
转换函数签名
import {
transformSync,
transformAsync,
transform,
transformFileSync,
transformFileAsync,
parseSync,
parseAsync,
} from "@babel/core";
// 同步转换代码字符串
transformSync(
code: string,
options?: TransformOptions
): BabelFileResult | null;
// 异步转换代码字符串(支持异步插件)
transformAsync(
code: string,
options?: TransformOptions
): Promise<BabelFileResult | null>;
// 回调形式(较少使用)
transform(
code: string,
options: TransformOptions | undefined,
callback: (err: Error | null, result: BabelFileResult | null) => void
): void;
// 同步转换文件(自动读取文件内容)
transformFileSync(
filename: string,
options?: TransformOptions
): BabelFileResult | null;
// 异步转换文件
transformFileAsync(
filename: string,
options?: TransformOptions
): Promise<BabelFileResult | null>;
// 只解析,不转换(同步)
parseSync(
code: string,
options?: TransformOptions
): t.File | null;
// 只解析,不转换(异步)
parseAsync(
code: string,
options?: TransformOptions
): Promise<t.File | null>;
BabelFileResult 接口
interface BabelFileResult {
// 生成的代码(code: false 时为 null)
code: string | null | undefined;
// source map 对象(sourceMaps: false 时为 null)
map: object | null | undefined;
// AST(ast: true 时才有值)
ast: t.File | null | undefined;
// 外部依赖(被 ignore 匹配到的文件)
ignored?: boolean;
// 外部依赖文件路径
externalDependencies?: Set<string>;
}
TransformOptions 接口(重要字段)
interface TransformOptions {
// ---- 输入文件信息 ----
// 关联的文件名(影响配置文件查找、source map、错误信息)
filename?: string;
// 相对文件名(用于 source map 的 sources 字段)
filenameRelative?: string;
// 工作目录(配置文件相对路径基准)
cwd?: string;
// ---- 输出控制 ----
// 是否生成 code 字符串(默认 true)
code?: boolean;
// 是否在结果中包含 AST(默认 false,开启后 ast 字段有值)
ast?: boolean;
// 是否克隆输入 AST(默认 true,避免 traverse 修改原始 AST)
cloneInputAst?: boolean;
// ---- 插件与预设 ----
// 插件列表
// 可以是:字符串、[字符串, options]、插件函数、[插件函数, options]
plugins?: PluginItem[];
// 预设列表(与 plugins 格式相同)
presets?: PluginItem[];
// ---- 语法模式 ----
// 模块类型(同 ParserOptions.sourceType)
sourceType?: "script" | "module" | "commonjs" | "unambiguous";
// ---- 配置文件 ----
// 项目根配置文件路径(false 禁用)
configFile?: string | false | boolean;
// 是否搜索 .babelrc(默认 true)
babelrc?: boolean;
// 哪些包允许搜索 babelrc
babelrcRoots?: boolean | MatchPattern | MatchPattern[];
// 项目根目录
root?: string;
// 根目录查找模式
rootMode?: "root" | "upward" | "upward-optional";
// 当前环境(读取 env[envName] 的配置,默认读 NODE_ENV 或 "development")
envName?: string;
// ---- Source Map ----
// 是否生成 source map
// true: 独立 map 对象;"inline": 追加到代码末尾;"both": 两者都要
sourceMaps?: boolean | "inline" | "both";
// source map 中的文件名
sourceFileName?: string;
// source map 根路径
sourceRoot?: string;
// ---- 代码生成 ----
// 保留原始行号(默认 false)
retainLines?: boolean;
// 紧凑输出(默认 "auto")
compact?: boolean | "auto";
// 压缩输出(默认 false)
minified?: boolean;
// 是否保留注释(默认 true)
comments?: boolean;
// 自定义注释过滤
shouldPrintComment?: (comment: string) => boolean;
// 传给 parser 的额外选项
parserOpts?: ParserOptions;
// 传给 generator 的额外选项
generatorOpts?: GeneratorOptions;
// ---- 条件配置 ----
// 环境特定配置
env?: { [envKey: string]: TransformOptions };
// 条件覆盖配置
overrides?: TransformOptions[];
// 包含匹配的文件(与 test 同义)
test?: MatchPattern | MatchPattern[];
include?: MatchPattern | MatchPattern[];
// 排除匹配的文件
exclude?: MatchPattern | MatchPattern[];
ignore?: MatchPattern | MatchPattern[];
only?: MatchPattern | MatchPattern[];
// ---- 优化 ----
// Babel 可以对代码做的假设(用于优化输出体积)
assumptions?: { [assumption: string]: boolean };
// 目标环境(支持 browserslist 格式)
targets?: string | string[] | { [key: string]: string };
}
PluginObj 接口(插件格式)
编写 Babel 插件时,插件函数返回一个 PluginObj:
import { PluginObj, PluginPass } from "@babel/core";
import traverse, { NodePath } from "@babel/traverse";
import * as t from "@babel/types";
// S 是 state 的类型,默认是 PluginPass
// PluginPass 是 Babel 传给插件的内置 state 对象
type PluginObj<S extends PluginPass = PluginPass> = {
// 插件名称(可选,用于错误信息)
name?: string;
// 在所有转换开始前调用(this 是 state 实例)
pre?: (this: S, file: BabelFile) => void | Promise<void>;
// AST 访问者(核心)
visitor?: Visitor<S>;
// 在所有转换完成后调用
post?: (this: S, file: BabelFile) => void | Promise<void>;
// 修改 parser/generator 选项
manipulateOptions?: (
options: TransformOptions,
parserOpts: ParserOptions
) => void;
// 继承另一个插件
inherits?: PluginAPI;
};
// PluginPass 是 state 的内置类型(插件 visitor 里的 this 和 state 参数)
class PluginPass {
file: BabelFile;
key: string | undefined | null;
opts: object; // 插件的配置选项
cwd: string; // 工作目录
filename: string | undefined; // 当前文件路径
// 存/取任意数据(跨 visitor 共享状态)
set(key: unknown, val: unknown): void;
get(key: unknown): any;
// 判断是否可以使用某个 helper
availableHelper(name: string, versionRange?: string): boolean;
// 添加 helper import(插件注入 helper 函数时使用)
addHelper(name: string): t.Expression;
// 生成带代码位置的错误信息
buildCodeFrameError(node: t.Node, msg: string, Error?: typeof Error): Error;
}
TypeScript 使用模式
1. 正确为 NodePath 加泛型
import traverse, { NodePath } from "@babel/traverse";
import * as t from "@babel/types";
const ast = parse(`foo(1, 2)`);
traverse(ast, {
// visitor 键名决定了 path 的泛型类型
// 这里 path 自动被推断为 NodePath<t.CallExpression>
CallExpression(path) {
// path.node 类型是 t.CallExpression,有完整类型提示
const callee = path.node.callee; // t.Expression | t.Super | t.V8IntrinsicIdentifier
const args = path.node.arguments; // (t.Expression | t.SpreadElement | ...)[]
},
// 也可以显式标注(当 TypeScript 推断失败时)
"StringLiteral|NumericLiteral"(path: NodePath<t.StringLiteral | t.NumericLiteral>) {
console.log(path.node.value);
},
});
2. 用 isXxx() 做类型收窄
traverse(ast, {
CallExpression(path) {
const callee = path.node.callee;
// callee 类型是 t.Expression | t.Super | t.V8IntrinsicIdentifier
// 直接访问 .name 会报错
// 用 isXxx 收窄
if (t.isIdentifier(callee)) {
// 这里 callee 类型收窄为 t.Identifier
console.log(callee.name); // 类型安全
}
if (t.isMemberExpression(callee)) {
// 这里 callee 类型收窄为 t.MemberExpression
// 继续收窄 property
if (!callee.computed && t.isIdentifier(callee.property)) {
console.log(callee.property.name);
}
}
// 带 opts 的收窄(同时检查属性值)
if (t.isIdentifier(callee, { name: "console" })) {
// callee 是名为 "console" 的 Identifier
}
},
});
3. state 对象的类型标注
import { NodePath, Visitor } from "@babel/traverse";
import * as t from "@babel/types";
// 定义 state 类型
interface MyState {
functionCount: number;
stringLiterals: string[];
filename: string;
}
const initialState: MyState = {
functionCount: 0,
stringLiterals: [],
filename: "",
};
// 方式一:traverse 的泛型形式(state 完全类型安全)
traverse<MyState>(
ast,
{
FunctionDeclaration(path, state) {
// state 类型是 MyState,有类型提示
state.functionCount++;
},
StringLiteral(path, state) {
state.stringLiterals.push(path.node.value);
},
},
undefined,
initialState
);
console.log(initialState.functionCount); // 访问结果
// 方式二:定义 Visitor 变量(复用 visitor)
const myVisitor: Visitor<MyState> = {
FunctionDeclaration(path, state) {
state.functionCount++;
},
StringLiteral(path, state) {
state.stringLiterals.push(path.node.value);
},
};
traverse<MyState>(ast, myVisitor, undefined, initialState);
4. 编写类型安全的 Babel 插件
import { PluginObj, PluginPass } from "@babel/core";
import * as t from "@babel/types";
// 定义插件选项类型
interface MyPluginOptions {
stripConsole?: boolean;
targetVar?: string;
}
// 扩展 PluginPass 以添加自定义字段
interface MyPluginState extends PluginPass {
opts: MyPluginOptions; // 覆盖 opts 的类型
removedCount: number;
}
// 导出插件函数
export default function myPlugin(): PluginObj<MyPluginState> {
return {
name: "my-plugin",
pre(this: MyPluginState) {
this.removedCount = 0;
},
visitor: {
ExpressionStatement(path, state) {
if (!state.opts.stripConsole) return;
const expr = path.node.expression;
if (
t.isCallExpression(expr) &&
t.isMemberExpression(expr.callee) &&
t.isIdentifier(expr.callee.object, { name: "console" })
) {
path.remove();
state.removedCount++;
}
},
Identifier(path, state) {
const targetVar = state.opts.targetVar;
if (!targetVar) return;
if (
path.node.name === targetVar &&
path.isReferenced()
) {
path.replaceWith(t.identifier("__renamed__" + targetVar));
}
},
},
post(this: MyPluginState) {
if (this.opts.stripConsole) {
console.info(`Removed ${this.removedCount} console calls`);
}
},
};
}
5. 遍历时避免 any 的最佳实践
import traverse, { NodePath } from "@babel/traverse";
import * as t from "@babel/types";
// 不好的写法:丢失类型信息
traverse(ast, {
CallExpression(path: any) { // any 丢失所有类型检查
path.node.callee.name; // 不安全,callee 不一定有 name
},
});
// 好的写法一:利用 visitor 键名自动推断
traverse(ast, {
CallExpression(path) { // path 自动推断为 NodePath<t.CallExpression>
const { callee } = path.node;
if (t.isIdentifier(callee)) { // 用 isXxx 收窄
callee.name; // 类型安全
}
},
});
// 好的写法二:提取为具名函数,显式标注类型
function visitCallExpression(path: NodePath<t.CallExpression>): void {
const { callee, arguments: args } = path.node;
if (t.isIdentifier(callee)) {
console.log(`调用函数: ${callee.name}`);
} else if (t.isMemberExpression(callee) && !callee.computed) {
t.assertIdentifier(callee.object);
t.assertIdentifier(callee.property);
console.log(`调用方法: ${callee.object.name}.${callee.property.name}`);
}
}
traverse(ast, { CallExpression: visitCallExpression });
// 好的写法三:处理 NodePath 数组(path.get 返回联合类型)
traverse(ast, {
ArrayExpression(path) {
const elements = path.get("elements"); // NodePath<...>[]
if (!Array.isArray(elements)) return;
for (const element of elements) {
if (element.isStringLiteral()) {
// element 收窄为 NodePath<t.StringLiteral>
console.log(element.node.value);
}
}
},
});
6. 处理 MemberExpression 的计算属性问题
由于 MemberExpression 是联合类型,property 的类型取决于 computed 字段:
traverse(ast, {
MemberExpression(path) {
const { node } = path;
if (node.computed) {
// node.property 类型是 t.Expression
// node 类型收窄为 MemberExpressionComputed
console.log("计算属性:", node.property.type);
} else {
// node.property 类型是 t.Identifier | t.PrivateName
if (t.isIdentifier(node.property)) {
console.log("属性名:", node.property.name);
}
}
},
});
7. 使用 path.scope 做安全重命名
import traverse from "@babel/traverse";
traverse(ast, {
Identifier(path) {
const { name } = path.node;
if (!/^_0x[0-9a-f]+$/i.test(name)) return;
const binding = path.scope.getBinding(name);
if (!binding) return;
// 确认是声明处(避免重复重命名)
if (binding.path.node !== path.node) return;
// generateUid 保证新名称在当前作用域不冲突
const newName = path.scope.generateUid("var");
// rename 自动更新所有引用(binding.referencePaths)
path.scope.rename(name, newName);
},
});
8. 完整的 TypeScript 工作流示例
import { parse } from "@babel/parser";
import traverse, { NodePath } from "@babel/traverse";
import generate from "@babel/generator";
import * as t from "@babel/types";
const sourceCode = `
import { foo } from "bar";
const result = foo(1, "hello", true);
`;
// 1. 解析(返回类型明确为 t.File)
const ast: t.File = parse(sourceCode, {
sourceType: "module",
plugins: ["typescript"],
});
// 2. 遍历并修改
traverse(ast, {
// ImportDeclaration 的 path 类型为 NodePath<t.ImportDeclaration>
ImportDeclaration(path) {
// source.value 是字符串,类型安全
if (path.node.source.value === "bar") {
path.node.source = t.stringLiteral("baz");
}
},
// CallExpression 的 path 类型为 NodePath<t.CallExpression>
CallExpression(path) {
const { callee, arguments: args } = path.node;
if (!t.isIdentifier(callee, { name: "foo" })) return;
// 将所有参数中的字符串字面量转为大写
const newArgs = args.map((arg) => {
if (t.isStringLiteral(arg)) {
return t.stringLiteral(arg.value.toUpperCase());
}
return arg;
});
path.replaceWith(
t.callExpression(callee, newArgs)
);
path.skip(); // 防止递归处理新节点
},
});
// 3. 生成代码
const { code, map } = generate(ast, {
comments: true,
jsescOption: { minimal: true },
});
console.log(code);
// import { foo } from "baz";
// const result = foo(1, "HELLO", true);
注意事项与踩坑
@babel/traverse 的默认导出问题
// 编译到 CJS 时,traverse 是 module.exports.default
// 需要这样导入(配合 esModuleInterop: true):
import traverse from "@babel/traverse";
// 如果不用 esModuleInterop,需要这样:
import _traverse from "@babel/traverse";
const traverse = (_traverse as any).default ?? _traverse;
// @babel/generator 同理
import _generate from "@babel/generator";
const generate = (_generate as any).default ?? _generate;
NodePath.get() 的返回类型
// path.get() 返回 NodePath | NodePath[]
// 当字段是数组时返回数组,需要用 Array.isArray 判断
const bodyPath = path.get("body");
if (Array.isArray(bodyPath)) {
// bodyPath 是 NodePath[]
bodyPath.forEach((p) => { /* ... */ });
} else {
// bodyPath 是 NodePath
}
cloneNode 用于避免节点共享问题
// 同一个节点对象不能挂载在树的两处
// 错误写法:
const id = t.identifier("foo");
const decl = t.variableDeclaration("const", [
t.variableDeclarator(id, null),
t.variableDeclarator(id, null), // 共享 id,会报错
]);
// 正确写法:
const decl2 = t.variableDeclaration("const", [
t.variableDeclarator(t.identifier("foo"), null),
t.variableDeclarator(t.cloneNode(t.identifier("foo")), null),
]);
replaceWith 后立即 skip()
traverse(ast, {
StringLiteral(path) {
// 替换为新的 StringLiteral 会再次触发本 visitor,造成无限循环
path.replaceWith(t.stringLiteral(path.node.value.trim()));
path.skip(); // 跳过新节点的遍历,打破循环
},
});
最佳实践
使用 TypeScript 类型缩小节点类型:在 @babel/traverse 的 Visitor 函数中,收到的 path 是联合类型;用类型守卫(if (t.isIdentifier(path.node)))缩小后,IDE 会自动补全节点的具体属性,避免手写字符串访问。
用 t.is* 系列函数做运行时类型检查:t.isBinaryExpression(node) 等函数比手动 node.type === 'BinaryExpression' 更安全——@babel/types 会处理别名(如 t.isExpression 匹配所有表达式类型),减少遗漏情况。
构建节点时用 Builder 函数:不要手写 { type: 'Identifier', name: 'x' },用 t.identifier('x') 让 Babel 自动填充所有必须字段(extra、位置信息等),避免缺少必须字段导致代码生成失败。
TypeScript 项目中导入 @babel/types 的 Node 联合类型:import type { Node, Statement, Expression } from '@babel/types' 为所有节点的联合类型,可以在自定义函数参数类型中使用,得到完整的类型推断。
利用 assertXxx 系列做防御式编程:t.assertIdentifier(node) 在节点类型不匹配时抛 TypeError,在调试阶段替代手动 if (!t.isIdentifier(node)) throw new Error(...) 更简洁;生产代码改用 t.isIdentifier 做条件分支。
常见陷阱
陷阱:path.node 类型太宽导致属性访问报 TypeScript 错误
现象: path.node.name 报 Property 'name' does not exist on type 'Node'。
原因: path.node 是 Node 联合类型,只有 Identifier 有 name 属性;TypeScript 不知道当前是哪个具体类型。
解决: 在 Visitor 中使用对应的 key(如 Identifier(path) { ... }),TypeScript 会自动推断 path.node 为 Identifier 类型;或用 if (t.isIdentifier(path.node)) 类型守卫。
陷阱:NodePath 与 Node 混用导致错误
现象: 将 path 传入期望 Node 的函数,或将 path.node 传入期望 NodePath 的方法。
原因: NodePath 包含 node(AST 节点)+ 作用域信息 + 父级引用等;它们是不同的对象,不可互换。
解决: 规范命名:path 是 NodePath,node 是 Node;需要节点时用 path.node,需要路径操作(replace、skip)时用 path。
陷阱:t.cloneNode 浅拷贝共享子节点引用
现象: 修改克隆节点的子节点,原节点也被修改。
原因: t.cloneNode(node) 默认浅拷贝,子节点还是同一引用;t.cloneNode(node, true) 才是深拷贝。
解决: 需要独立副本时用 t.cloneDeepWithoutLoc(node) 或 t.cloneNode(node, true)(第二参数为 true 深拷贝)。