ast-types

中文文档
0.16.3发布于 2026/08/31MITTypeScriptCommonJS构建来源已验证近 30 天下载 2.8亿

Esprima与执行Mozilla JS Parser API相容

中文使用文档
文档目录7 节
  1. 安装
  2. 基本用途
  3. 类型脚本
  4. AST 倾斜
  5. 节点Path
  6. 范围
  7. 自定义 AST 节点类型

AST 类型 CI npm

本单元提供高效、模块化、, 埃斯皮里亚- 执行不兼容 页:1 抽象语法 树 类型等级 创始人: 莫齐拉·帕塞尔 API 密码。 。 。 .

安装

npm install ast-types

货包本身的TypeScript申报;没有 @types/ast-types 要安装。 TypeScript 4.4 及以后的支持,以及 对申报的每份申报都按4.4、4.9和5.x分类.

继续工作 ast-types 本身:

git clone https://github.com/benjamn/ast-types.git
cd ast-types
npm install
npm test

基本用途

import assert from "assert";
import {
  namedTypes as n,
  builders as b,
} from "ast-types";

var fooId = b.identifier("foo");
var ifFoo = b.ifStatement(fooId, b.blockStatement([
  b.expressionStatement(b.callExpression(fooId, []))
]));

assert.ok(n.IfStatement.check(ifFoo));
assert.ok(n.Statement.check(ifFoo));
assert.ok(n.Node.check(ifFoo));

assert.ok(n.BlockStatement.check(ifFoo.consequent));
assert.strictEqual(
  ifFoo.consequent.body[0].expression.arguments.length,
  0,
);

assert.strictEqual(ifFoo.test, fooId);
assert.ok(n.Expression.check(ifFoo.test));
assert.ok(n.Identifier.check(ifFoo.test));
assert.ok(!n.Statement.check(ifFoo.test));

类型脚本

所发表的宣言来自与《公约》相同的定义 运行时间类型, 因此每个节点类型都可以作为类型Script类型 同名词下 namedTypes,以及 NodePath 财务报告和审定财务报表 Type 通用 :

import {
  builders as b,
  namedTypes as n,
  visit,
  NodePath,
} from "ast-types";

const fooId: n.Identifier = b.identifier("foo");
const call: n.CallExpression = b.callExpression(fooId, []);

// Visitor methods receive a NodePath parameterized by the node type, so
// path.node is narrowed for you.
visit(call, {
  visitCallExpression(path) {
    const callee: n.CallExpression["callee"] = path.node.callee;
    this.traverse(path);
  },
});

declare const somePath: NodePath<n.CallExpression>;
somePath.node.arguments;

注意节点类型中的 TypeScript 类型描述定义 接受,这往往是一个工会。 拓宽一个字段以承认一个节点形状 消费者可能需要缩小范围 变动情况 对于那些已经发生的.

AST 倾斜

因为它如此透彻地理解了AST类型系统,这个库 能够提供极佳的节点迭接和倒转机制.

如果你想要完全控制 转弯,你只需要一个方法 列出您 AST 节点的已知字段并获取其 价值,你可能会对原始人感兴趣 getFieldNames 财务报告和审定财务报表 getFieldValue数字 :

import {
  getFieldNames,
  getFieldValue,
} from "ast-types";

const partialFunExpr = { type: "FunctionExpression" };

// Even though partialFunExpr doesn't actually contain all the fields that
// are expected for a FunctionExpression, types.getFieldNames knows:
console.log(getFieldNames(partialFunExpr));
// [ 'type', 'id', 'params', 'body', 'generator', 'expression',
//   'defaults', 'rest', 'async' ]

// For fields that have default values, types.getFieldValue will return
// the default if the field is not actually defined.
console.log(getFieldValue(partialFunExpr, "generator"));
// false

两个低级的助手功能, eachField 财务报告和审定财务报表 someField,为 定义 getFieldNames 财务报告和审定财务报表 getFieldValue数字 :

// Iterate over all defined fields of an object, including those missing
// or undefined, passing each field name and effective value (as returned
// by getFieldValue) to the callback. If the object has no corresponding
// Def, the callback will never be called.
export function eachField(object, callback, context) {
  getFieldNames(object).forEach(function(name) {
    callback.call(this, name, getFieldValue(object, name));
  }, context);
}

// Similar to eachField, except that iteration stops as soon as the
// callback returns a truthy value. Like Array.prototype.some, the final
// result is either true or false to indicates whether the callback
// returned true for any element or not.
export function someField(object, callback, context) {
  return getFieldNames(object).some(function(name) {
    return callback.call(this, name, getFieldValue(object, name));
  }, context);
}

这就是你如何复制一个AST节点:

import { eachField } from "ast-types";
const copy = {};
eachField(node, function(name, value) {
  // Note that undefined fields will be visited too, according to
  // the rules associated with node.type, and default field values
  // will be substituted if appropriate.
  copy[name] = value;
})

但还不止这些! 您也可以轻松访问整个语法树 强者 types.visit 抽象化.

举个小例子来说明.. arguments.callee 从未在 ast数字 :

import assert from "assert";
import {
  visit,
  namedTypes as n,
} from "ast-types";

visit(ast, {
  // This method will be called for any node with .type "MemberExpression":
  visitMemberExpression(path) {
    // Visitor methods receive a single argument, a NodePath object
    // wrapping the node of interest.
    var node = path.node;

    if (
      n.Identifier.check(node.object) &&
      node.object.name === "arguments" &&
      n.Identifier.check(node.property)
    ) {
      assert.notStrictEqual(node.property.name, "callee");
    }

    // It's your responsibility to call this.traverse with some
    // NodePath object (usually the one passed into the visitor
    // method) before the visitor method returns, or return false to
    // indicate that the traversal need not continue any further down
    // this subtree.
    this.traverse(path);
  }
});

这里有一个 稍多参与的例子 改造 ...rest 参数进入浏览器运行的 ES5 JavaScript :

import assert from "assert";
import {
  builders as b,
  namedTypes as n,
  visit,
} from "ast-types";

// Reuse the same AST structure for Array.prototype.slice.call.
var sliceExpr = b.memberExpression(
  b.memberExpression(
    b.memberExpression(
      b.identifier("Array"),
      b.identifier("prototype"),
      false
    ),
    b.identifier("slice"),
    false
  ),
  b.identifier("call"),
  false
);

visit(ast, {
  // This method will be called for any node whose type is a subtype of
  // Function (e.g., FunctionDeclaration, FunctionExpression, and
  // ArrowFunctionExpression). Note that types.visit precomputes a
  // lookup table from every known type to the appropriate visitor
  // method to call for nodes of that type, so the dispatch takes
  // constant time.
  visitFunction(path) {
    // Visitor methods receive a single argument, a NodePath object
    // wrapping the node of interest.
    const node = path.node;

    // It's your responsibility to call this.traverse with some
    // NodePath object (usually the one passed into the visitor
    // method) before the visitor method returns, or return false to
    // indicate that the traversal need not continue any further down
    // this subtree. An assertion will fail if you forget, which is
    // awesome, because it means you will never again make the
    // disastrous mistake of forgetting to traverse a subtree. Also
    // cool: because you can call this method at any point in the
    // visitor method, it's up to you whether your traversal is
    // pre-order, post-order, or both!
    this.traverse(path);

    // This traversal is only concerned with Function nodes that have
    // rest parameters.
    if (!node.rest) {
      return;
    }

    // For the purposes of this example, we won't worry about functions
    // with Expression bodies.
    n.BlockStatement.assert(node.body);

    // Use types.builders to build a variable declaration of the form
    //
    //   var rest = Array.prototype.slice.call(arguments, n);
    //
    // where `rest` is the name of the rest parameter, and `n` is a
    // numeric literal specifying the number of named parameters the
    // function takes.
    const restVarDecl = b.variableDeclaration("var", [
      b.variableDeclarator(
        node.rest,
        b.callExpression(sliceExpr, [
          b.identifier("arguments"),
          b.literal(node.params.length)
        ])
      )
    ]);

    // Similar to doing node.body.body.unshift(restVarDecl), except
    // that the other NodePath objects wrapping body statements will
    // have their indexes updated to accommodate the new statement.
    path.get("body", "body").unshift(restVarDecl);

    // Nullify node.rest now that we have simulated the behavior of
    // the rest parameter using ordinary JavaScript.
    path.get("rest").replace(null);

    // There's nothing wrong with doing node.rest = null, but I wanted
    // to point out that the above statement has the same effect.
    assert.strictEqual(node.rest, null);
  }
});

这是你可能使用 types.visit 用于执行一个函数 确定给定函数节点是否指的是 this数字 :

function usesThis(funcNode) {
  n.Function.assert(funcNode);
  var result = false;

  visit(funcNode, {
    visitThisExpression(path) {
      result = true;

      // The quickest way to terminate the traversal is to call
      // this.abort(), which throws a special exception (instanceof
      // this.AbortRequest) that will be caught in the top-level
      // types.visit method, so you don't have to worry about
      // catching the exception yourself.
      this.abort();
    },

    visitFunction(path) {
      // ThisExpression nodes in nested scopes don't count as `this`
      // references for the original function node, so we can safely
      // avoid traversing this subtree.
      return false;
    },

    visitCallExpression(path) {
      const node = path.node;

      // If the function contains CallExpression nodes involving
      // super, those expressions will implicitly depend on the
      // value of `this`, even though they do not explicitly contain
      // any ThisExpression nodes.
      if (this.isSuperCallExpression(node)) {
        result = true;
        this.abort(); // Throws AbortRequest exception.
      }

      this.traverse(path);
    },

    // Yes, you can define arbitrary helper methods.
    isSuperCallExpression(callExpr) {
      n.CallExpression.assert(callExpr);
      return this.isSuperIdentifier(callExpr.callee)
          || this.isSuperMemberExpression(callExpr.callee);
    },

    // And even helper helper methods!
    isSuperIdentifier(node) {
      return n.Identifier.check(node.callee)
          && node.callee.name === "super";
    },

    isSuperMemberExpression(node) {
      return n.MemberExpression.check(node.callee)
          && n.Identifier.check(node.callee.object)
          && node.callee.object.name === "super";
    }
  });

  return result;
}

你可能猜到,当一个 AbortRequest 是从树下抛出 例外将从相应的呼叫中传播到 this.traverse 在祖先的访客方法。 如果你决定取消 请求,只需抓住例外,并调用它 .cancel() 方法。 这个 其余树在树下 try- 说吧catch 块将废弃,但 祖先节点的其余兄弟姐妹将仍然被访问.

节点Path

这个 NodePath 对象传递给访问者的方法是 AST 周围的包装器 节点,它可以提供对祖先物体链的连接 (一路回AST根)和范围信息.

总的来说, path.node 是指被包裹的节点, path.parent.node 指最近一个 Node 祖师相传, path.parent.parent.node 页:1 外祖相等相传.

请注意: path.node 可能不是 path.parent.node例如,情况可能是: path.node 实值 属于父节点直接子的数组元素:

path.node === path.parent.node.elements[3]

在这种情况下,你应该知道 path.parentPath 提供 更精细地访问对象的完整路径(不只是 Node (一) 从AST的根部:

// In reality, path.parent is the grandparent of path:
path.parentPath.parentPath === path.parent

// The path.parentPath object wraps the elements array (note that we use
// .value because the elements array is not a Node):
path.parentPath.value === path.parent.node.elements

// The path.node object is the fourth element in that array:
path.parentPath.value[3] === path.node

// Unlike path.node and path.value, which are synonyms because path.node
// is a Node object, path.parentPath.node is distinct from
// path.parentPath.value, because the elements array is not a
// Node. Instead, path.parentPath.node refers to the closest ancestor
// Node, which happens to be the same as path.parent.node:
path.parentPath.node === path.parent.node

// The path is named for its index in the elements array:
path.name === 3

// Likewise, path.parentPath is named for the property by which
// path.parent.node refers to it:
path.parentPath.name === "elements"

// Putting it all together, we can follow the chain of object references
// from path.parent.node all the way to path.node by accessing each
// property by name:
path.parent.node[path.parentPath.name][path.name] === path.node

这些 NodePath 对象是在无 修改 AST 节点本身, 因此如果相同的节点没有问题 多次出现在 AST 中( 如 Array.prototype.slice.call 内 因为它将用微调访问 NodePath 每次出现的时候.

儿童 NodePath 通过调用 .get 方法 父母关系 NodePath 对象 :

// If a NodePath object for the elements array has never been created
// before, it will be created here and cached in the future:
path.get("elements").get(3).value === path.value.elements[3]

// Alternatively, you can pass multiple property names to .get instead of
// chaining multiple .get calls:
path.get("elements", 0).value === path.value.elements[0]

NodePath 对象支持一些有用的方法:

// Replace one node with another node:
var fifth = path.get("elements", 4);
fifth.replace(newNode);

// Now do some stuff that might rearrange the list, and this replacement
// remains safe:
fifth.replace(newerNode);

// Replace the third element in an array with two new nodes:
path.get("elements", 2).replace(
  b.identifier("foo"),
  b.thisExpression()
);

// Remove a node and its parent if it would leave a redundant AST node:
//e.g. var t = 1, y =2; removing the `t` and `y` declarators results in `var undefined`.
path.prune(); //returns the closest parent `NodePath`.

// Remove a node from a list of nodes:
path.get("elements", 3).replace();

// Add three new nodes to the beginning of a list of nodes:
path.get("elements").unshift(a, b, c);

// Remove and return the first node in a list of nodes:
path.get("elements").shift();

// Push two new nodes onto the end of a list of nodes:
path.get("elements").push(d, e);

// Remove and return the last node in a list of nodes:
path.get("elements").pop();

// Insert a new node before/after the seventh node in a list of nodes:
var seventh = path.get("elements", 6);
seventh.insertBefore(newNode);
seventh.insertAfter(newNode);

// Insert a new element at index 5 in a list of nodes:
path.get("elements").insertAt(5, newNode);

范围

显示为 path.scope 在AST轨道提供时 关于可变和功能声明的信息 包含 path.node。见 范围.ts 为公众服务 接口,目前包括 .isGlobal, (中文(简体) ), .getGlobalScope(), (中文(简体) ), .depth, (中文(简体) ), .declares(name), (中文(简体) ), .lookup(name),以及 .getBindings()。 。 。 .

自定义 AST 节点类型

这个 ast-types 模块设计要扩展。 为此 为指定新的AST节点类型提供可读、可声明的语法, 主要基于 require("ast-types").Type.def 函数 :

import {
  Type,
  builtInTypes,
  builders as b,
  finalize,
} from "ast-types";

const { def } = Type;
const { string } = builtInTypes;

// Suppose you need a named File type to wrap your Programs.
def("File")
  .bases("Node")
  .build("name", "program")
  .field("name", string)
  .field("program", def("Program"));

// Prevent further modifications to the File type (and any other
// types newly introduced by def(...)).
finalize();

// The b.file builder function is now available. It expects two
// arguments, as named by .build("name", "program") above.
const main = b.file("main.js", b.program([
  // Pointless program contents included for extra color.
  b.functionDeclaration(b.identifier("succ"), [
    b.identifier("x")
  ], b.blockStatement([
    b.returnStatement(
      b.binaryExpression(
        "+", b.identifier("x"), b.literal(1)
      )
    )
  ]))
]));

assert.strictEqual(main.name, "main.js");
assert.strictEqual(main.program.body[0].params[0].name, "x");
// etc.

// If you pass the wrong type of arguments, or fail to pass enough
// arguments, an AssertionError will be thrown.

b.file(b.blockStatement([]));
// ==> AssertionError: {"body":[],"type":"BlockStatement","loc":null} does not match type string

b.file("lib/types.js", b.thisExpression());
// ==> AssertionError: {"type":"ThisExpression","loc":null} does not match type Program

这个 def 语法用于定义所有默认 AST 节点类型 bayl-core.ts (英语), (中文(简体) ), 贝贝.ts, (中文(简体) ), 核心数.t, (中文(简体) ), 提议, (中文(简体) ), es6. 吨级, (中文(简体) ), es2016.ts (英语), (中文(简体) ), es2017.ts (英语), (中文(简体) ), 2018.ts (英语), (中文(简体) ), es2019.ts (英语), (中文(简体) ), es2020.ts (英语), (中文(简体) ), es2021.ts (英语), (中文(简体) ), es2022.ts (英语), (中文(简体) ), 双子宫, (中文(简体) ), 流量.ts, (中文(简体) ), 页:1, (中文(简体) ), 类型注释.ts, (中文(简体) ), 财务报告和审定财务报表 类型标记.ts, (中文(简体) ), 所以,你已经 并不缺乏可以学习的例子.

版本与发布记录

ast-types 共发布 175 个版本,首次发布于 2013/04/25;过去 12 个月发布了 3 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.16.3latest2026/08/312 个月前
0.16.2next2026/08/312 个月前
0.16.2-rc.02026/08/312 个月前
0.16.12022/12/143 年前
0.16.02022/12/123 年前
0.15.22021/12/034 年前

关于 ast-types 的常见问题

ast-types 是什么?

Esprima与执行Mozilla JS Parser API相容

如何安装 ast-types?

在项目目录运行 npm install ast-types,或使用 pnpm add / yarn add / bun add。

ast-types 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

ast-types 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

ast-types 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=4。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

ast-types 可以商用吗?

ast-types 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

ast-types 的下载量有多少?

根据 npm 官方下载统计,ast-types 在 2026/10/05 – 2026/11/03 这 30 天内被下载 282,741,975 次,日均约 9,424,733 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

ast-types 还在维护吗?

最新版本 0.16.3 发布于 2026/08/31(2 个月前);至今共发布 175 个版本,其中过去 12 个月发布了 3 个;首次发布于 2013/04/25。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。