or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

eslint-scope.mdeslint-visitor-keys.mdespree.mdindex.md
tile.json

eslint-visitor-keys.mddocs/

ESLint Visitor Keys

ESLint Visitor Keys provides constants and utilities for traversing JavaScript Abstract Syntax Trees (AST). It defines which properties of AST nodes contain child nodes, enabling systematic traversal of the syntax tree for static analysis tools.

Core Imports

import { getKeys, unionWith, KEYS } from "eslint-visitor-keys";

For CommonJS:

const { getKeys, unionWith, KEYS } = require("eslint-visitor-keys");

Default import:

import * as visitorKeys from "eslint-visitor-keys";

Basic Usage

import * as espree from "espree";
import { getKeys, KEYS } from "eslint-visitor-keys";

// Parse JavaScript code
const ast = espree.parse(`
  function test(x) {
    if (x > 0) {
      return x + 1;
    }
    return 0;
  }
`, { ecmaVersion: 2022 });

// Get visitor keys for the Program node
const programKeys = getKeys(ast);
console.log(programKeys); // ["body"]

// Get keys for a function declaration
const functionDecl = ast.body[0];
const functionKeys = getKeys(functionDecl);
console.log(functionKeys); // ["id", "params", "body"]

// Access complete visitor keys mapping
console.log(KEYS.FunctionDeclaration); // ["id", "params", "body"]
console.log(KEYS.IfStatement); // ["test", "consequent", "alternate"]

Capabilities

Visitor Key Retrieval

Get visitor keys for individual AST nodes with automatic filtering of metadata properties.

/**
 * Get visitor keys for an AST node
 * @param {Object} node - AST node
 * @returns {readonly string[]} Array of property names that contain child nodes
 */
function getKeys(node);

Visitor Key Extension

Merge additional visitor keys with the default set for custom AST node types.

/**
 * Merge additional keys with default visitor keys
 * @param {VisitorKeys} additionalKeys - Additional visitor key mappings
 * @returns {VisitorKeys} Combined visitor keys
 */
function unionWith(additionalKeys);

Complete Visitor Keys Mapping

Access to the complete mapping of AST node types to their child node properties.

/** Complete mapping of AST node types to their child node keys */
const KEYS: VisitorKeys;

interface VisitorKeys {
  readonly [nodeType: string]: readonly string[];
}

Supported AST Node Types

The visitor keys cover all standard ECMAScript AST node types and common extensions:

Core ES5+ Nodes

// Program and statements
Program: ["body"];
BlockStatement: ["body"];
ExpressionStatement: ["expression"];
EmptyStatement: [];
DebuggerStatement: [];

// Declarations
FunctionDeclaration: ["id", "params", "body"];
VariableDeclaration: ["declarations"];
VariableDeclarator: ["id", "init"];

// Control flow
IfStatement: ["test", "consequent", "alternate"];
SwitchStatement: ["discriminant", "cases"];
SwitchCase: ["test", "consequent"];
WhileStatement: ["test", "body"];
DoWhileStatement: ["body", "test"];
ForStatement: ["init", "test", "update", "body"];
ForInStatement: ["left", "right", "body"];

ES6+ Features

// Arrow functions and classes
ArrowFunctionExpression: ["params", "body"];
ClassDeclaration: ["id", "superClass", "body"];
ClassExpression: ["id", "superClass", "body"];
ClassBody: ["body"];
MethodDefinition: ["key", "value"];

// Destructuring and spread
ObjectPattern: ["properties"];
ArrayPattern: ["elements"];
RestElement: ["argument"];
SpreadElement: ["argument"];

// Modules
ImportDeclaration: ["specifiers", "source"];
ImportSpecifier: ["imported", "local"];
ImportDefaultSpecifier: ["local"];
ImportNamespaceSpecifier: ["local"];
ExportNamedDeclaration: ["declaration", "specifiers", "source"];
ExportDefaultDeclaration: ["declaration"];
ExportAllDeclaration: ["source"];

Modern ECMAScript Features

// ES2017+
AwaitExpression: ["argument"];
AsyncFunctionDeclaration: ["id", "params", "body"];
AsyncFunctionExpression: ["id", "params", "body"];

// ES2018+
ForOfStatement: ["left", "right", "body"];
ObjectExpression: ["properties"];
Property: ["key", "value"];

// ES2020+
ChainExpression: ["expression"];
ImportExpression: ["source"];
OptionalCallExpression: ["callee", "arguments"];
OptionalMemberExpression: ["object", "property"];

// ES2021+
PrivateIdentifier: [];

// ES2022+
StaticBlock: ["body"];
PropertyDefinition: ["key", "value"];

JSX Extensions

// JSX nodes
JSXElement: ["openingElement", "children", "closingElement"];
JSXFragment: ["openingFragment", "children", "closingFragment"];
JSXOpeningElement: ["name", "attributes"];
JSXClosingElement: ["name"];
JSXAttribute: ["name", "value"];
JSXSpreadAttribute: ["argument"];
JSXExpressionContainer: ["expression"];
JSXText: [];

TypeScript Extensions

// TypeScript-specific nodes
TSTypeAnnotation: ["typeAnnotation"];
TSTypeParameterDeclaration: ["params"];
TSTypeParameter: ["constraint", "default"];
TSInterfaceDeclaration: ["id", "typeParameters", "extends", "body"];
TSTypeAliasDeclaration: ["id", "typeParameters", "typeAnnotation"];

Usage Examples

Basic AST traversal:

import * as espree from "espree";
import { getKeys } from "eslint-visitor-keys";

function traverse(node, visitor) {
  if (typeof visitor[node.type] === "function") {
    visitor[node.type](node);
  }
  
  const keys = getKeys(node);
  for (const key of keys) {
    const child = node[key];
    if (Array.isArray(child)) {
      for (const item of child) {
        if (item && typeof item === "object") {
          traverse(item, visitor);
        }
      }
    } else if (child && typeof child === "object") {
      traverse(child, visitor);
    }
  }
}

const ast = espree.parse("function test() { return 42; }", { ecmaVersion: 2022 });

traverse(ast, {
  FunctionDeclaration(node) {
    console.log(`Found function: ${node.id.name}`);
  },
  ReturnStatement(node) {
    console.log("Found return statement");
  }
});

Custom visitor keys:

import { unionWith, KEYS } from "eslint-visitor-keys";

// Add custom AST node types
const customKeys = unionWith({
  CustomNode: ["customProperty", "children"],
  AnotherCustomNode: ["value", "metadata"]
});

console.log(customKeys.CustomNode); // ["customProperty", "children"]
console.log(customKeys.FunctionDeclaration); // ["id", "params", "body"] (from default keys)

Filtering metadata properties:

import { getKeys } from "eslint-visitor-keys";

const node = {
  type: "Identifier",
  name: "foo",
  parent: null,        // Filtered out
  range: [0, 3],       // Filtered out
  loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 3 } }, // Filtered out
  leadingComments: [], // Filtered out
  trailingComments: [], // Filtered out
  _custom: "data"      // Filtered out (starts with underscore)
};

const keys = getKeys(node);
console.log(keys); // [] (no child node properties for Identifier)

// Compare with raw Object.keys()
console.log(Object.keys(node)); // ["type", "name", "parent", "range", "loc", "leadingComments", "trailingComments", "_custom"]

Working with complex expressions:

import * as espree from "espree";
import { getKeys } from "eslint-visitor-keys";

const ast = espree.parse("obj?.prop?.method?.(arg1, arg2)", { 
  ecmaVersion: 2022,
  sourceType: "module"
});

function printNodeStructure(node, depth = 0) {
  const indent = "  ".repeat(depth);
  console.log(`${indent}${node.type}`);
  
  const keys = getKeys(node);
  for (const key of keys) {
    const child = node[key];
    console.log(`${indent}  ${key}:`);
    
    if (Array.isArray(child)) {
      child.forEach((item, index) => {
        if (item && typeof item === "object") {
          console.log(`${indent}    [${index}]:`);
          printNodeStructure(item, depth + 3);
        }
      });
    } else if (child && typeof child === "object") {
      printNodeStructure(child, depth + 2);
    } else {
      console.log(`${indent}    ${child}`);
    }
  }
}

printNodeStructure(ast.body[0].expression);
// Output shows the structure of the optional chaining expression

ESLint rule integration:

// Example ESLint rule using visitor keys
module.exports = {
  create(context) {
    function checkNode(node) {
      const keys = getKeys(node);
      
      // Process each child node
      keys.forEach(key => {
        const child = node[key];
        if (Array.isArray(child)) {
          child.forEach(item => {
            if (item && typeof item === "object") {
              // Process child node
              checkNode(item);
            }
          });
        } else if (child && typeof child === "object") {
          checkNode(child);
        }
      });
    }
    
    return {
      Program(node) {
        checkNode(node);
      }
    };
  }
};

Filtered Properties

The getKeys() function automatically filters out metadata properties that are not part of the AST structure:

  • parent - Parent node reference
  • leadingComments - Comments before the node
  • trailingComments - Comments after the node
  • Properties starting with _ - Private/internal properties
  • Location information (loc, range, start, end) - Position metadata

TypeScript Integration

eslint-visitor-keys includes TypeScript type definitions:

import { getKeys, unionWith, KEYS, VisitorKeys } from "eslint-visitor-keys";

// Type definitions are included
const keys: readonly string[] = getKeys(node);
const customKeys: VisitorKeys = unionWith({ CustomNode: ["children"] });
const functionKeys: readonly string[] = KEYS.FunctionDeclaration;

Performance Considerations

  • Visitor keys are statically defined and cached for optimal performance
  • getKeys() performs minimal runtime filtering
  • The KEYS object provides O(1) lookup for known node types
  • Custom keys from unionWith() are merged efficiently

Integration with AST Tools

eslint-visitor-keys integrates seamlessly with popular AST processing tools:

  • ESLint: Core dependency for rule traversal
  • Esprima/Espree: Provides visitor keys for generated ASTs
  • Acorn: Compatible with acorn-generated ASTs
  • @babel/parser: Works with Babel ASTs (with custom key extensions)
  • typescript: Supports TypeScript AST nodes