CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-typescript-eslint--utils

Utilities for working with TypeScript + ESLint together

Pending
Overview
Eval results
Files

ast-utils.mddocs/

AST Utilities

The ASTUtils namespace provides comprehensive utilities for Abstract Syntax Tree manipulation and analysis in TypeScript ESLint rules.

Import

import { ASTUtils } from '@typescript-eslint/utils';

Node Type Predicates

Basic Type Checking

// Generic node type checker
function isNodeOfType<NodeType extends AST_NODE_TYPES>(
  nodeType: NodeType
): (node: TSESTree.Node | null | undefined) => node is Extract<TSESTree.Node, { type: NodeType }>;

// Multiple node types checker  
function isNodeOfTypes<NodeTypes extends readonly AST_NODE_TYPES[]>(
  nodeTypes: NodeTypes
): (node: TSESTree.Node | null | undefined) => node is Extract<TSESTree.Node, { type: NodeTypes[number] }>;

// Usage examples
const isFunctionDeclaration = ASTUtils.isNodeOfType(AST_NODE_TYPES.FunctionDeclaration);
const isVariableOrFunction = ASTUtils.isNodeOfTypes([
  AST_NODE_TYPES.VariableDeclaration,
  AST_NODE_TYPES.FunctionDeclaration
]);

if (isFunctionDeclaration(node)) {
  // node is typed as TSESTree.FunctionDeclaration
  console.log(node.id?.name);
}

Conditional Type Checking

// Node type with additional conditions
function isNodeOfTypeWithConditions<
  NodeType extends AST_NODE_TYPES,
  ExtractedNode extends Extract<TSESTree.Node, { type: NodeType }>,
  Conditions extends Partial<ExtractedNode>
>(
  nodeType: NodeType,
  conditions: Conditions
): (node: TSESTree.Node | null | undefined) => node is ExtractedNode & Conditions;

// Usage example
const isAsyncFunction = ASTUtils.isNodeOfTypeWithConditions(
  AST_NODE_TYPES.FunctionDeclaration,
  { async: true }
);

if (isAsyncFunction(node)) {
  // node is TSESTree.FunctionDeclaration with async: true
}

Function Predicates

// Check for any function node type
function isFunction(node: TSESTree.Node | null | undefined): node is TSESTree.Function;

// Check for TypeScript function type
function isFunctionType(node: TSESTree.Node | null | undefined): node is TSESTree.TSFunctionType;

// Check for function or function type
function isFunctionOrFunctionType(
  node: TSESTree.Node | null | undefined
): node is TSESTree.Function | TSESTree.TSFunctionType;

// Specific TypeScript function types
function isTSFunctionType(node: TSESTree.Node | null | undefined): node is TSESTree.TSFunctionType;
function isTSConstructorType(node: TSESTree.Node | null | undefined): node is TSESTree.TSConstructorType;

// Usage examples
if (ASTUtils.isFunction(node)) {
  // Handle arrow functions, function declarations, function expressions
  const params = node.params;
  const body = node.body;
}

if (ASTUtils.isFunctionType(node)) {
  // Handle TypeScript function type annotations
  const returnType = node.returnType;
}

Expression Predicates

// Optional chaining call expressions
function isOptionalCallExpression(node: TSESTree.Node | null | undefined): node is TSESTree.CallExpression & { optional: true };

// Logical OR expressions
function isLogicalOrOperator(node: TSESTree.Node | null | undefined): node is TSESTree.LogicalExpression & { operator: '||' };

// Type assertions
function isTypeAssertion(node: TSESTree.Node | null | undefined): node is TSESTree.TSTypeAssertion | TSESTree.TSAsExpression;

// Await expressions
function isAwaitExpression(node: TSESTree.Node | null | undefined): node is TSESTree.AwaitExpression;

// Usage examples
if (ASTUtils.isOptionalCallExpression(node)) {
  // Handle obj?.method() calls
  const callee = node.callee; // has optional property
}

if (ASTUtils.isTypeAssertion(node)) {
  // Handle both <Type>expr and expr as Type
  const expression = node.expression;  
}

Statement and Declaration Predicates

// Variable declarators
function isVariableDeclarator(node: TSESTree.Node | null | undefined): node is TSESTree.VariableDeclarator;

// Loop statements
function isLoop(node: TSESTree.Node | null | undefined): node is TSESTree.DoWhileStatement | TSESTree.ForStatement | TSESTree.ForInStatement | TSESTree.ForOfStatement | TSESTree.WhileStatement;

// Identifiers
function isIdentifier(node: TSESTree.Node | null | undefined): node is TSESTree.Identifier;

// Usage examples  
if (ASTUtils.isLoop(node)) {
  // Handle all loop types uniformly
  const body = node.body;
}

if (ASTUtils.isVariableDeclarator(node)) {
  // Handle variable declarations
  const id = node.id;
  const init = node.init;
}

Class and Method Predicates

// Class or type element nodes
function isClassOrTypeElement(node: TSESTree.Node | null | undefined): node is TSESTree.ClassElement | TSESTree.TypeElement;

// Constructor methods
function isConstructor(node: TSESTree.Node | null | undefined): node is TSESTree.MethodDefinition & { kind: 'constructor' };

// Setter methods
function isSetter(node: TSESTree.Node | undefined): node is TSESTree.MethodDefinition & { kind: 'set' } | TSESTree.PropertyDefinition & { kind: 'set' };

// Usage examples
if (ASTUtils.isConstructor(node)) {
  // Handle constructor methods specifically
  const params = node.value.params;
}

if (ASTUtils.isSetter(node)) {
  // Handle setter methods
  const key = node.key;
}

Token Predicates

Punctuator Tokens

// Optional chaining punctuator (?.)
function isOptionalChainPunctuator(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '?.' };
function isNotOptionalChainPunctuator(token: TSESTree.Token | null | undefined): token is Exclude<TSESTree.Token, TSESTree.PunctuatorToken & { value: '?.' }>;

// Non-null assertion punctuator (!)
function isNonNullAssertionPunctuator(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '!' };
function isNotNonNullAssertionPunctuator(token: TSESTree.Token | null | undefined): token is Exclude<TSESTree.Token, TSESTree.PunctuatorToken & { value: '!' }>;

// Common punctuators
function isArrowToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '=>' };
function isCommaToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: ',' };
function isSemicolonToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: ';' };
function isColonToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: ':' };

// Usage examples
const tokens = sourceCode.getTokens(node);
const arrowToken = tokens.find(ASTUtils.isArrowToken);
if (arrowToken) {
  // Handle arrow function tokens
}

Bracket and Brace Tokens

// Opening tokens
function isOpeningBraceToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '{' };
function isOpeningBracketToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '[' };
function isOpeningParenToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '(' };

// Closing tokens
function isClosingBraceToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: '}' };
function isClosingBracketToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: ']' };
function isClosingParenToken(token: TSESTree.Token | null | undefined): token is TSESTree.PunctuatorToken & { value: ')' };

// Negated versions available for all punctuators
function isNotOpeningBraceToken(token: TSESTree.Token | null | undefined): boolean;
function isNotClosingParenToken(token: TSESTree.Token | null | undefined): boolean;
// ... (similar pattern for all punctuators)

// Usage examples
const openBrace = sourceCode.getTokenAfter(node, ASTUtils.isOpeningBraceToken);
const closeBrace = sourceCode.getTokenAfter(node, ASTUtils.isClosingBraceToken);

Keyword Tokens

// Keyword token predicates
function isAwaitKeyword(token: TSESTree.Token | null | undefined): token is TSESTree.KeywordToken & { value: 'await' };
function isTypeKeyword(token: TSESTree.Token | null | undefined): token is TSESTree.KeywordToken & { value: 'type' };
function isImportKeyword(token: TSESTree.Token | null | undefined): token is TSESTree.KeywordToken & { value: 'import' };

// Usage examples
const awaitToken = sourceCode.getFirstToken(node, ASTUtils.isAwaitKeyword);
if (awaitToken) {
  // Handle await keyword positioning
}

Comment Tokens

// Comment token predicates
function isCommentToken(token: TSESTree.Token | null | undefined): token is TSESTree.Comment;
function isNotCommentToken(token: TSESTree.Token | null | undefined): token is Exclude<TSESTree.Token, TSESTree.Comment>;

// Usage examples
const nonCommentTokens = sourceCode.getTokens(node).filter(ASTUtils.isNotCommentToken);

Token Conditions

Advanced Token Filtering

// Token type with conditions
function isTokenOfTypeWithConditions<
  TokenType extends AST_TOKEN_TYPES,
  ExtractedToken extends Extract<TSESTree.Token, { type: TokenType }>,
  Conditions extends Partial<ExtractedToken>
>(
  tokenType: TokenType,
  conditions: Conditions
): (token: TSESTree.Token | null | undefined) => token is ExtractedToken & Conditions;

// Negated token conditions
function isNotTokenOfTypeWithConditions<
  TokenType extends AST_TOKEN_TYPES,
  ExtractedToken extends Extract<TSESTree.Token, { type: TokenType }>,
  Conditions extends Partial<ExtractedToken>
>(
  tokenType: TokenType,
  conditions: Conditions
): (token: TSESTree.Token | null | undefined) => token is Exclude<TSESTree.Token, ExtractedToken & Conditions>;

// Usage examples
const isSpecificPunctuator = ASTUtils.isTokenOfTypeWithConditions(
  AST_TOKEN_TYPES.Punctuator,
  { value: '=>' as const }
);

const isNotArrowFunction = ASTUtils.isNotTokenOfTypeWithConditions(
  AST_TOKEN_TYPES.Punctuator,
  { value: '=>' as const }
);

Utility Functions

Line Analysis

// Check if tokens are on the same line
function isTokenOnSameLine(
  left: TSESTree.Node | TSESTree.Token,
  right: TSESTree.Node | TSESTree.Token
): boolean;

// Line break matcher regex
const LINEBREAK_MATCHER: RegExp; // /\r\n|[\r\n\u2028\u2029]/

// Usage examples
if (ASTUtils.isTokenOnSameLine(node, nextToken)) {
  // Tokens are on the same line
}

const hasLineBreak = ASTUtils.LINEBREAK_MATCHER.test(sourceCode.getText(node));

ESLint Integration Utilities

Function Analysis

// Get function head location for reporting
function getFunctionHeadLocation(
  node: TSESTree.Function,
  sourceCode: TSESLint.SourceCode
): TSESTree.SourceLocation;

// Get function name with kind description
function getFunctionNameWithKind(
  node: TSESTree.Function,
  sourceCode?: TSESLint.SourceCode
): string;

// Usage examples
const location = ASTUtils.getFunctionHeadLocation(node, context.getSourceCode());
context.report({
  loc: location,
  messageId: 'error'
});

const functionDescription = ASTUtils.getFunctionNameWithKind(node);
// Returns strings like "function 'myFunc'", "arrow function", "method 'myMethod'"

Property Analysis

// Get property name from property-like nodes
function getPropertyName(
  node: TSESTree.Property | TSESTree.MethodDefinition | TSESTree.PropertyDefinition,
  initialScope?: TSESLint.Scope.Scope
): string | null;

// Usage examples
const propName = ASTUtils.getPropertyName(node);
if (propName) {
  // Handle known property names
}

Static Value Analysis

// Get static value if determinable
function getStaticValue(
  node: TSESTree.Node,
  initialScope?: TSESLint.Scope.Scope
): { value: unknown } | null;

// Get string value if constant
function getStringIfConstant(
  node: TSESTree.Node,
  initialScope?: TSESLint.Scope.Scope
): string | null;

// Usage examples
const staticValue = ASTUtils.getStaticValue(node);
if (staticValue) {
  console.log('Static value:', staticValue.value);
}

const stringValue = ASTUtils.getStringIfConstant(node);
if (stringValue) {
  // Handle constant string values
}

Side Effect Analysis

// Check if node has side effects
function hasSideEffect(
  node: TSESTree.Node,
  sourceCode: TSESLint.SourceCode,
  options?: { considerGetters?: boolean; considerImplicitTypeConversion?: boolean }
): boolean;

// Usage examples
if (!ASTUtils.hasSideEffect(node, context.getSourceCode())) {
  // Safe to remove or reorder this node
}

Parentheses Analysis

// Check if node is parenthesized (overloaded)
function isParenthesized(node: TSESTree.Node, sourceCode: TSESLint.SourceCode): boolean;
function isParenthesized(times: number, node: TSESTree.Node, sourceCode: TSESLint.SourceCode): boolean;

// Usage examples  
if (ASTUtils.isParenthesized(node, sourceCode)) {
  // Node has parentheses around it
}

if (ASTUtils.isParenthesized(2, node, sourceCode)) {
  // Node has at least 2 levels of parentheses: ((node))
}

Pattern Matching

PatternMatcher Class

class PatternMatcher {
  constructor(pattern: RegExp, options?: { escaped?: boolean });
  
  // Execute pattern matching
  execAll(str: string): IterableIterator<RegExpExecArray>;
  
  // Test if pattern matches
  test(str: string): boolean;
  
  // Replace matches in string  
  [Symbol.replace](str: string, replacer: string | ((match: string, ...args: any[]) => string)): string;
}

// Usage examples
const matcher = new ASTUtils.PatternMatcher(/function\s+(\w+)/g);

for (const match of matcher.execAll(sourceCode.getText())) {
  console.log('Found function:', match[1]);
}

Reference Tracking

ReferenceTracker Class

class ReferenceTracker {
  constructor(globalScope: TSESLint.Scope.Scope, options?: {
    mode?: 'strict' | 'legacy';
    globalObjectNames?: readonly string[];
  });
  
  // Iterate over global references
  iterateGlobalReferences<T>(
    traceMap: ReferenceTracker.TraceMap<T>
  ): IterableIterator<ReferenceTracker.FoundReference<T>>;
  
  // Iterate over CommonJS references
  iterateCjsReferences<T>(
    traceMap: ReferenceTracker.TraceMap<T>
  ): IterableIterator<ReferenceTracker.FoundReference<T>>;
  
  // Iterate over ES module references
  iterateEsModuleReferences<T>(
    traceMap: ReferenceTracker.TraceMap<T>
  ): IterableIterator<ReferenceTracker.FoundReference<T>>;
}

// Usage examples
const tracker = new ASTUtils.ReferenceTracker(context.getScope());

const traceMap = {
  React: {
    createElement: { [ASTUtils.ReferenceTracker.READ]: true }
  }
};

for (const { node, path } of tracker.iterateGlobalReferences(traceMap)) {
  // Handle React.createElement references
}

Scope Analysis

Variable and Scope Utilities

// Find variable by name or node
function findVariable(
  initialScope: TSESLint.Scope.Scope,
  nameOrNode: string | TSESTree.Identifier
): TSESLint.Scope.Variable | null;

// Get innermost scope containing node
function getInnermostScope(
  initialScope: TSESLint.Scope.Scope,
  node: TSESTree.Node
): TSESLint.Scope.Scope;

// Usage examples
const variable = ASTUtils.findVariable(context.getScope(), 'myVar');
if (variable) {
  // Check variable references and definitions
  const refs = variable.references;
  const defs = variable.defs;
}

const innerScope = ASTUtils.getInnermostScope(context.getScope(), node);
// Get scope that directly contains the node

Complete Usage Example

import { ESLintUtils, ASTUtils, TSESTree } from '@typescript-eslint/utils';

const createRule = ESLintUtils.RuleCreator(name => `https://example.com/${name}`);

export default createRule({
  name: 'comprehensive-ast-example',
  meta: {
    type: 'suggestion',
    docs: { description: 'Comprehensive AST utilities example' },
    messages: {
      optionalCall: 'Consider using optional chaining',
      parenthesized: 'Unnecessary parentheses detected',
      sideEffect: 'Expression has side effects'
    },
    schema: []
  },
  defaultOptions: [],
  create(context) {
    const sourceCode = context.getSourceCode();
    
    return {
      CallExpression(node) {
        // Check for optional calls
        if (ASTUtils.isOptionalCallExpression(node)) {
          context.report({
            node,
            messageId: 'optionalCall'
          });
        }
        
        // Check if parenthesized
        if (ASTUtils.isParenthesized(node, sourceCode)) {
          context.report({
            node,
            messageId: 'parenthesized'
          });
        }
        
        // Check for side effects
        if (ASTUtils.hasSideEffect(node, sourceCode)) {
          context.report({
            node,
            messageId: 'sideEffect'
          });
        }
      },
      
      'Program:exit'() {
        // Use reference tracker
        const tracker = new ASTUtils.ReferenceTracker(context.getScope());
        const traceMap = {
          console: {
            log: { [ASTUtils.ReferenceTracker.READ]: true }
          }
        };
        
        for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
          // Handle console.log references
        }
      }
    };
  }
});

Install with Tessl CLI

npx tessl i tessl/npm-typescript-eslint--utils

docs

ast-utils.md

eslint-utils.md

index.md

json-schema.md

ts-eslint.md

ts-utils.md

tile.json