Skip to content

⬅️ Back to Table of Contents

📄 node-utils

📊 Analysis Summary

Metric Count
🔧 Functions 43
🧱 Classes 1
📦 Imports 6
📊 Variables & Constants 5
📐 Interfaces 1
📑 Type Aliases 5

📚 Table of Contents

🛠️ File Location:

📂 packages/typescript-estree/src/node-utils.ts

📦 Imports

Name Source
TSESTree ./ts-estree
getModifiers ./getModifiers
xhtmlEntities ./jsx/xhtml-entities
AST_NODE_TYPES ./ts-estree
AST_TOKEN_TYPES ./ts-estree
typescriptVersionIsAtLeast ./version-check

Variables & Constants

Name Type Kind Value Exported
isAtLeast50 boolean const typescriptVersionIsAtLeast['5.0']
SyntaxKind any const ts.SyntaxKind
LOGICAL_OPERATORS ReadonlySet<LogicalOperatorKind> const new Set([ SyntaxKind.AmpersandAmpersandToken, SyntaxKind.BarBarToken, SyntaxK...
ASSIGNMENT_OPERATORS ReadonlySet<AssignmentOperatorKind> const new Set([ ts.SyntaxKind.AmpersandAmpersandEqualsToken, ts.SyntaxKind.Ampersan...
BINARY_OPERATORS ReadonlySet<BinaryOperatorKind> const new Set([ SyntaxKind.AmpersandAmpersandToken, SyntaxKind.AmpersandToken, Synt...

Functions

isLogicalOperator(operator: ts.BinaryOperatorToken): operator is ts.Token<LogicalOperatorKind>

Returns true if the given ts.Token is a logical operator

Raw JSDoc
/**
 * Returns true if the given ts.Token is a logical operator
 */

Calls:

  • (LOGICAL_OPERATORS as ReadonlySet<ts.SyntaxKind>).has
Code
export function isLogicalOperator(
  operator: ts.BinaryOperatorToken,
): operator is ts.Token<LogicalOperatorKind> {
  return (LOGICAL_OPERATORS as ReadonlySet<ts.SyntaxKind>).has(operator.kind);
}

isESTreeBinaryOperator(operator: ts.BinaryOperatorToken): operator is ts.Token<BinaryOperatorKind>

Parameters:

  • operator ts.BinaryOperatorToken

Returns: operator is ts.Token<BinaryOperatorKind>

Calls:

  • (BINARY_OPERATORS as ReadonlySet<ts.SyntaxKind>).has
Code
export function isESTreeBinaryOperator(
  operator: ts.BinaryOperatorToken,
): operator is ts.Token<BinaryOperatorKind> {
  return (BINARY_OPERATORS as ReadonlySet<ts.SyntaxKind>).has(operator.kind);
}

getTextForTokenKind(kind: T): TokenForTokenKind<T>

Returns the string form of the given TSToken SyntaxKind

Raw JSDoc
/**
 * Returns the string form of the given TSToken SyntaxKind
 */

Calls:

  • ts.tokenToString
Code
export function getTextForTokenKind<T extends ts.SyntaxKind>(
  kind: T,
): TokenForTokenKind<T> {
  return ts.tokenToString(kind) as T extends keyof TokenToText
    ? TokenToText[T]
    : string | undefined;
}

isESTreeClassMember(node: ts.Node): boolean

Returns true if the given ts.Node is a valid ESTree class member

Raw JSDoc
/**
 * Returns true if the given ts.Node is a valid ESTree class member
 */
Code
export function isESTreeClassMember(node: ts.Node): boolean {
  return node.kind !== SyntaxKind.SemicolonClassElement;
}

hasModifier(modifierKind: ts.KeywordSyntaxKind, node: ts.Node): boolean

Checks if a ts.Node has a modifier

Raw JSDoc
/**
 * Checks if a ts.Node has a modifier
 */

Calls:

  • getModifiers (from ./getModifiers)
  • modifiers?.some
Code
export function hasModifier(
  modifierKind: ts.KeywordSyntaxKind,
  node: ts.Node,
): boolean {
  const modifiers = getModifiers(node);
  return modifiers?.some(modifier => modifier.kind === modifierKind) === true;
}

getLastModifier(node: ts.Node): ts.Modifier | null

Get last last modifier in ast

Returns: undefined returns last modifier if present or null

Raw JSDoc
/**
 * Get last last modifier in ast
 * @returns returns last modifier if present or null
 */

Calls:

  • getModifiers (from ./getModifiers)
Code
export function getLastModifier(node: ts.Node): ts.Modifier | null {
  const modifiers = getModifiers(node);
  if (modifiers == null) {
    return null;
  }
  return modifiers[modifiers.length - 1] ?? null;
}

isComma(token: ts.Node): token is ts.Token<ts.SyntaxKind.CommaToken>

Returns true if the given ts.Token is a comma

Raw JSDoc
/**
 * Returns true if the given ts.Token is a comma
 */
Code
export function isComma(
  token: ts.Node,
): token is ts.Token<ts.SyntaxKind.CommaToken> {
  return token.kind === SyntaxKind.CommaToken;
}

isComment(node: ts.Node): boolean

Returns true if the given ts.Node is a comment

Raw JSDoc
/**
 * Returns true if the given ts.Node is a comment
 */
Code
export function isComment(node: ts.Node): boolean {
  return (
    node.kind === SyntaxKind.SingleLineCommentTrivia ||
    node.kind === SyntaxKind.MultiLineCommentTrivia
  );
}

getBinaryExpressionType(operator: ts.BinaryOperatorToken): | { operator: TokenForTokenKind<AssignmentOperatorKind>; ty…

Returns the binary expression type of the given ts.Token

Raw JSDoc
/**
 * Returns the binary expression type of the given ts.Token
 */

Calls:

  • isAssignmentOperator
  • getTextForTokenKind
  • isLogicalOperator
  • isESTreeBinaryOperator
  • ts.tokenToString
Code
export function getBinaryExpressionType(operator: ts.BinaryOperatorToken):
  | {
      operator: TokenForTokenKind<AssignmentOperatorKind>;
      type: AST_NODE_TYPES.AssignmentExpression;
    }
  | {
      operator: TokenForTokenKind<BinaryOperatorKind>;
      type: AST_NODE_TYPES.BinaryExpression;
    }
  | {
      operator: TokenForTokenKind<LogicalOperatorKind>;
      type: AST_NODE_TYPES.LogicalExpression;
    } {
  if (isAssignmentOperator(operator)) {
    return {
      type: AST_NODE_TYPES.AssignmentExpression,
      operator: getTextForTokenKind(operator.kind),
    };
  }

  if (isLogicalOperator(operator)) {
    return {
      type: AST_NODE_TYPES.LogicalExpression,
      operator: getTextForTokenKind(operator.kind),
    };
  }

  if (isESTreeBinaryOperator(operator)) {
    return {
      type: AST_NODE_TYPES.BinaryExpression,
      operator: getTextForTokenKind(operator.kind),
    };
  }

  throw new Error(
    `Unexpected binary operator ${ts.tokenToString(operator.kind)}`,
  );
}

getLineAndCharacterFor(pos: number, ast: ts.SourceFile): TSESTree.Position

Returns line and column data for the given positions

Raw JSDoc
/**
 * Returns line and column data for the given positions
 */

Calls:

  • ast.getLineAndCharacterOfPosition
Code
export function getLineAndCharacterFor(
  pos: number,
  ast: ts.SourceFile,
): TSESTree.Position {
  const loc = ast.getLineAndCharacterOfPosition(pos);
  return {
    column: loc.character,
    line: loc.line + 1,
  };
}

getLocFor(range: TSESTree.Range, ast: ts.SourceFile): TSESTree.SourceLocation

Returns line and column data for the given start and end positions, for the given AST

Raw JSDoc
/**
 * Returns line and column data for the given start and end positions,
 * for the given AST
 */

Calls:

  • range.map
  • getLineAndCharacterFor
Code
export function getLocFor(
  range: TSESTree.Range,
  ast: ts.SourceFile,
): TSESTree.SourceLocation {
  const [start, end] = range.map(pos => getLineAndCharacterFor(pos, ast));
  return { end, start };
}

canContainDirective(node: ts.Block | ts.ClassStaticBlockDeclarati…): boolean

Check whatever node can contain directive

Raw JSDoc
/**
 * Check whatever node can contain directive
 */
Code
export function canContainDirective(
  node:
    ts.Block | ts.ClassStaticBlockDeclaration | ts.ModuleBlock | ts.SourceFile,
): boolean {
  if (node.kind === ts.SyntaxKind.Block) {
    switch (node.parent.kind) {
      case ts.SyntaxKind.Constructor:
      case ts.SyntaxKind.GetAccessor:
      case ts.SyntaxKind.SetAccessor:
      case ts.SyntaxKind.ArrowFunction:
      case ts.SyntaxKind.FunctionExpression:
      case ts.SyntaxKind.FunctionDeclaration:
      case ts.SyntaxKind.MethodDeclaration:
        return true;
      default:
        return false;
    }
  }
  return true;
}

getRange(node: Pick<ts.Node, 'getEnd' | 'getStart'>, ast: ts.SourceFile): [number, number]

Returns range for the given ts.Node

Raw JSDoc
/**
 * Returns range for the given ts.Node
 */

Calls:

  • node.getStart
  • node.getEnd
Code
export function getRange(
  node: Pick<ts.Node, 'getEnd' | 'getStart'>,
  ast: ts.SourceFile,
): [number, number] {
  return [node.getStart(ast), node.getEnd()];
}

isJSXToken(node: ts.Node): boolean

Returns true if a given ts.Node is a JSX token

Raw JSDoc
/**
 * Returns true if a given ts.Node is a JSX token
 */
Code
export function isJSXToken(node: ts.Node): boolean {
  return (
    node.kind >= SyntaxKind.JsxElement && node.kind <= SyntaxKind.JsxAttribute
  );
}

getDeclarationKind(node: ts.VariableDeclarationList): DeclarationKind

Returns the declaration kind of the given ts.Node

Raw JSDoc
/**
 * Returns the declaration kind of the given ts.Node
 */

Internal Comments:

// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison

Code
export function getDeclarationKind(
  node: ts.VariableDeclarationList,
): DeclarationKind {
  if (node.flags & ts.NodeFlags.Let) {
    return 'let';
  }
  // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
  if ((node.flags & ts.NodeFlags.AwaitUsing) === ts.NodeFlags.AwaitUsing) {
    return 'await using';
  }
  if (node.flags & ts.NodeFlags.Const) {
    return 'const';
  }
  if (node.flags & ts.NodeFlags.Using) {
    return 'using';
  }
  return 'var';
}

getTSNodeAccessibility(node: ts.Node): 'private' | 'protected' | 'public' | undefined

Gets a ts.Node's accessibility level

Raw JSDoc
/**
 * Gets a ts.Node's accessibility level
 */

Calls:

  • getModifiers (from ./getModifiers)
Code
export function getTSNodeAccessibility(
  node: ts.Node,
): 'private' | 'protected' | 'public' | undefined {
  const modifiers = getModifiers(node);
  if (modifiers == null) {
    return undefined;
  }
  for (const modifier of modifiers) {
    switch (modifier.kind) {
      case SyntaxKind.PublicKeyword:
        return 'public';
      case SyntaxKind.ProtectedKeyword:
        return 'protected';
      case SyntaxKind.PrivateKeyword:
        return 'private';
      default:
        break;
    }
  }
  return undefined;
}

findNextToken(previousToken: ts.TextRange, parent: ts.Node, ast: ts.SourceFile): ts.Node | undefined

Finds the next token based on the previous one and its parent Had to copy this from TS instead of using TS's version because theirs doesn't pass the ast to getChildren

Raw JSDoc
/**
 * Finds the next token based on the previous one and its parent
 * Had to copy this from TS instead of using TS's version because theirs doesn't pass the ast to getChildren
 */

Calls:

  • find
  • ts.isToken
  • firstDefined
  • n.getChildren
  • nodeHasTokens

Internal Comments:

// this is token that starts at the end of previous token - return it
// previous token is enclosed somewhere in the child (x2)
// previous token ends exactly at the beginning of child (x3)

Code
export function findNextToken(
  previousToken: ts.TextRange,
  parent: ts.Node,
  ast: ts.SourceFile,
): ts.Node | undefined {
  return find(parent);

  function find(n: ts.Node): ts.Node | undefined {
    if (ts.isToken(n) && n.pos === previousToken.end) {
      // this is token that starts at the end of previous token - return it
      return n;
    }
    return firstDefined(n.getChildren(ast), (child: ts.Node) => {
      const shouldDiveInChildNode =
        // previous token is enclosed somewhere in the child
        (child.pos <= previousToken.pos && child.end > previousToken.end) ||
        // previous token ends exactly at the beginning of child
        child.pos === previousToken.end;
      return shouldDiveInChildNode && nodeHasTokens(child, ast)
        ? find(child)
        : undefined;
    });
  }
}

findFirstMatchingAncestor(node: ts.Node, predicate: (node: ts.Node) => boolean): ts.Node | undefined

Find the first matching ancestor based on the given predicate function.

Parameters:

  • node any: The current ts.Node
  • predicate any: The predicate function to apply to each checked ancestor

Returns: undefined a matching parent ts.Node

Raw JSDoc
/**
 * Find the first matching ancestor based on the given predicate function.
 * @param node The current ts.Node
 * @param predicate The predicate function to apply to each checked ancestor
 * @returns a matching parent ts.Node
 */

Calls:

  • predicate
Code
export function findFirstMatchingAncestor(
  node: ts.Node,
  predicate: (node: ts.Node) => boolean,
): ts.Node | undefined {
  let current = node as ts.Node | undefined;
  while (current) {
    if (predicate(current)) {
      return current;
    }
    current = current.parent;
  }
  return undefined;
}

hasJSXAncestor(node: ts.Node): boolean

Returns true if a given ts.Node has a JSX token within its hierarchy

Raw JSDoc
/**
 * Returns true if a given ts.Node has a JSX token within its hierarchy
 */

Calls:

  • findFirstMatchingAncestor
Code
export function hasJSXAncestor(node: ts.Node): boolean {
  return !!findFirstMatchingAncestor(node, isJSXToken);
}

unescapeStringLiteralText(text: string): string

Unescape the text content of string literals, e.g. & -> &

Parameters:

  • text any: The escaped string literal text.

Returns: undefined The unescaped string literal text.

Raw JSDoc
/**
 * Unescape the text content of string literals, e.g. &amp; -> &
 * @param text The escaped string literal text.
 * @returns The unescaped string literal text.
 */

Calls:

  • text.replaceAll
  • entity.slice
  • parseInt
  • item.slice
  • String.fromCodePoint
Code
export function unescapeStringLiteralText(text: string): string {
  return text.replaceAll(/&(?:#\d+|#x[\da-fA-F]+|[0-9a-zA-Z]+);/g, entity => {
    const item = entity.slice(1, -1);
    if (item[0] === '#') {
      const codePoint =
        item[1] === 'x'
          ? parseInt(item.slice(2), 16)
          : parseInt(item.slice(1), 10);
      return codePoint > 0x10ffff // RangeError: Invalid code point
        ? entity
        : String.fromCodePoint(codePoint);
    }
    return xhtmlEntities[item] || entity;
  });
}

isComputedProperty(node: ts.Node): node is ts.ComputedPropertyName

Returns true if a given ts.Node is a computed property

Raw JSDoc
/**
 * Returns true if a given ts.Node is a computed property
 */
Code
export function isComputedProperty(
  node: ts.Node,
): node is ts.ComputedPropertyName {
  return node.kind === SyntaxKind.ComputedPropertyName;
}

isOptional(node: { questionToken?: ts.QuestionToken; }): boolean

Returns true if a given ts.Node is optional (has QuestionToken)

Parameters:

  • node any: ts.Node to be checked
Raw JSDoc
/**
 * Returns true if a given ts.Node is optional (has QuestionToken)
 * @param node ts.Node to be checked
 */
Code
export function isOptional(node: {
  questionToken?: ts.QuestionToken;
}): boolean {
  return !!node.questionToken;
}

isChainExpression(node: TSESTree.Node): node is TSESTree.ChainExpression

Returns true if the node is an optional chain node

Raw JSDoc
/**
 * Returns true if the node is an optional chain node
 */
Code
export function isChainExpression(
  node: TSESTree.Node,
): node is TSESTree.ChainExpression {
  return node.type === AST_NODE_TYPES.ChainExpression;
}

isChildUnwrappableOptionalChain(node: | ts.CallExpression | ts.ElementAccessE…, child: TSESTree.Node): boolean

Returns true of the child of property access expression is an optional chain

Raw JSDoc
/**
 * Returns true of the child of property access expression is an optional chain
 */

Calls:

  • isChainExpression

Internal Comments:

// (x?.y).z is semantically different, and as such .z is no longer optional (x4)

Code
export function isChildUnwrappableOptionalChain(
  node:
    | ts.CallExpression
    | ts.ElementAccessExpression
    | ts.NonNullExpression
    | ts.PropertyAccessExpression,
  child: TSESTree.Node,
): boolean {
  return (
    isChainExpression(child) &&
    // (x?.y).z is semantically different, and as such .z is no longer optional
    node.expression.kind !== ts.SyntaxKind.ParenthesizedExpression
  );
}

getTokenType(token: ts.Identifier | ts.Token<ts.SyntaxKind>): Exclude<AST_TOKEN_TYPES, AST_TOKEN_TYPES.Block | AST_TOKEN_…

Returns the type of a given ts.Token

Raw JSDoc

/**
 * Returns the type of a given ts.Token
 */

Calls:

  • isJSXToken
  • hasJSXAncestor

Internal Comments:

// A TypeScript-StringLiteral token with a TypeScript-JsxAttribute or TypeScript-JsxElement parent,
// must actually be an ESTree-JSXText token
// intentional fallthrough
// Some JSX tokens have to be determined based on their parent

Code
export function getTokenType(
  token: ts.Identifier | ts.Token<ts.SyntaxKind>,
): Exclude<AST_TOKEN_TYPES, AST_TOKEN_TYPES.Block | AST_TOKEN_TYPES.Line> {
  if (token.kind === SyntaxKind.NullKeyword) {
    return AST_TOKEN_TYPES.Null;
  }

  if (
    token.kind >= SyntaxKind.FirstKeyword &&
    token.kind <= SyntaxKind.LastFutureReservedWord
  ) {
    if (
      token.kind === SyntaxKind.FalseKeyword ||
      token.kind === SyntaxKind.TrueKeyword
    ) {
      return AST_TOKEN_TYPES.Boolean;
    }

    return AST_TOKEN_TYPES.Keyword;
  }

  if (
    token.kind >= SyntaxKind.FirstPunctuation &&
    token.kind <= SyntaxKind.LastPunctuation
  ) {
    return AST_TOKEN_TYPES.Punctuator;
  }

  if (
    token.kind >= SyntaxKind.NoSubstitutionTemplateLiteral &&
    token.kind <= SyntaxKind.TemplateTail
  ) {
    return AST_TOKEN_TYPES.Template;
  }

  switch (token.kind) {
    case SyntaxKind.NumericLiteral:
    case SyntaxKind.BigIntLiteral:
      return AST_TOKEN_TYPES.Numeric;

    case SyntaxKind.PrivateIdentifier:
      return AST_TOKEN_TYPES.PrivateIdentifier;

    case SyntaxKind.JsxText:
      return AST_TOKEN_TYPES.JSXText;

    case SyntaxKind.StringLiteral:
      // A TypeScript-StringLiteral token with a TypeScript-JsxAttribute or TypeScript-JsxElement parent,
      // must actually be an ESTree-JSXText token
      if (
        token.parent.kind === SyntaxKind.JsxAttribute ||
        token.parent.kind === SyntaxKind.JsxElement
      ) {
        return AST_TOKEN_TYPES.JSXText;
      }

      return AST_TOKEN_TYPES.String;

    case SyntaxKind.RegularExpressionLiteral:
      return AST_TOKEN_TYPES.RegularExpression;

    case SyntaxKind.Identifier:
    case SyntaxKind.ConstructorKeyword:
    case SyntaxKind.GetKeyword:
    case SyntaxKind.SetKeyword:

    // intentional fallthrough
    default:
  }

  // Some JSX tokens have to be determined based on their parent
  if (token.kind === SyntaxKind.Identifier) {
    if (isJSXToken(token.parent)) {
      return AST_TOKEN_TYPES.JSXIdentifier;
    }

    if (
      token.parent.kind === SyntaxKind.PropertyAccessExpression &&
      hasJSXAncestor(token)
    ) {
      return AST_TOKEN_TYPES.JSXIdentifier;
    }
  }

  return AST_TOKEN_TYPES.Identifier;
}

convertToken(token: ts.Token<ts.TokenSyntaxKind>, ast: ts.SourceFile): TSESTree.Token

Extends and formats a given ts.Token, for a given AST

Raw JSDoc
/**
 * Extends and formats a given ts.Token, for a given AST
 */

Calls:

  • token.getFullStart
  • token.getStart
  • token.getEnd
  • ast.text.slice
  • getTokenType
  • getLocFor
  • value.slice
  • value.lastIndexOf

Internal Comments:

// @ts-expect-error TS is complaining about `value` not being the correct
// type but it is

Code
export function convertToken(
  token: ts.Token<ts.TokenSyntaxKind>,
  ast: ts.SourceFile,
): TSESTree.Token {
  const start =
    token.kind === SyntaxKind.JsxText
      ? token.getFullStart()
      : token.getStart(ast);
  const end = token.getEnd();
  const value = ast.text.slice(start, end);
  const tokenType = getTokenType(token);
  const range: TSESTree.Range = [start, end];
  const loc = getLocFor(range, ast);

  if (tokenType === AST_TOKEN_TYPES.RegularExpression) {
    return {
      type: tokenType,
      loc,
      range,
      regex: {
        flags: value.slice(value.lastIndexOf('/') + 1),
        pattern: value.slice(1, value.lastIndexOf('/')),
      },
      value,
    };
  }

  if (tokenType === AST_TOKEN_TYPES.PrivateIdentifier) {
    return {
      type: tokenType,
      loc,
      range,
      value: value.slice(1),
    };
  }

  // @ts-expect-error TS is complaining about `value` not being the correct
  // type but it is
  return {
    type: tokenType,
    loc,
    range,
    value,
  };
}

convertTokens(ast: ts.SourceFile): TSESTree.Token[]

Converts all tokens for the given AST

Parameters:

  • ast any: the AST object

Returns: undefined the converted Tokens

Raw JSDoc
/**
 * Converts all tokens for the given AST
 * @param ast the AST object
 * @returns the converted Tokens
 */

Calls:

  • isComment
  • isJSDocComment
  • isToken
  • result.push
  • convertToken
  • node.getChildren(ast).forEach
  • walk

Internal Comments:

/**
   * @param node the ts.Node
   */
// TypeScript generates tokens for types in JSDoc blocks. Comment tokens
// and their children should not be walked or added to the resulting tokens list.

Code
export function convertTokens(ast: ts.SourceFile): TSESTree.Token[] {
  const result: TSESTree.Token[] = [];
  /**
   * @param node the ts.Node
   */
  function walk(node: ts.Node): void {
    // TypeScript generates tokens for types in JSDoc blocks. Comment tokens
    // and their children should not be walked or added to the resulting tokens list.
    if (isComment(node) || isJSDocComment(node)) {
      return;
    }

    if (isToken(node) && node.kind !== SyntaxKind.EndOfFileToken) {
      result.push(convertToken(node, ast));
    } else {
      node.getChildren(ast).forEach(walk);
    }
  }
  walk(ast);
  return result;
}

createError(node: ts.Node, message: string): TSError

Parameters:

  • node ts.Node
  • message string

Returns: TSError

Code
export function createError(node: ts.Node, message: string): TSError;

nodeHasTokens(n: ts.Node, ast: ts.SourceFile): boolean

Parameters:

  • n ts.Node
  • ast ts.SourceFile

Returns: boolean

Calls:

  • n.getWidth

Internal Comments:

// If we have a token or node that has a non-zero width, it must have tokens.
// Note: getWidth() does not take trivia into account.

Code
export function nodeHasTokens(n: ts.Node, ast: ts.SourceFile): boolean {
  // If we have a token or node that has a non-zero width, it must have tokens.
  // Note: getWidth() does not take trivia into account.
  return n.kind === SyntaxKind.EndOfFileToken
    ? !!(n as ts.JSDocContainer).jsDoc
    : n.getWidth(ast) !== 0;
}

firstDefined(array: readonly T[] | undefined, callback: (element: T, index: number) => U | unde…): U | undefined

Like forEach, but suitable for use with numbers and strings (which may be falsy).

Raw JSDoc
/**
 * Like `forEach`, but suitable for use with numbers and strings (which may be falsy).
 */

Calls:

  • callback

Internal Comments:

// eslint-disable-next-line @typescript-eslint/internal/eqeq-nullish (x2)

Code
export function firstDefined<T, U>(
  array: readonly T[] | undefined,
  callback: (element: T, index: number) => U | undefined,
): U | undefined {
  // eslint-disable-next-line @typescript-eslint/internal/eqeq-nullish
  if (array === undefined) {
    return undefined;
  }

  for (let i = 0; i < array.length; i++) {
    const result = callback(array[i], i);
    // eslint-disable-next-line @typescript-eslint/internal/eqeq-nullish
    if (result !== undefined) {
      return result;
    }
  }
  return undefined;
}

identifierIsThisKeyword(id: ts.Identifier): boolean

Parameters:

  • id ts.Identifier

Returns: boolean

Calls:

  • ts.identifierToKeywordKind
Code
export function identifierIsThisKeyword(id: ts.Identifier): boolean {
  return (
    (isAtLeast50
      ? ts.identifierToKeywordKind(id)
      : // @ts-expect-error -- intentional fallback for older TS versions <=4.9
        id.originalKeywordKind) === SyntaxKind.ThisKeyword
  );
}

isThisIdentifier(node: ts.Node | undefined): node is ts.Identifier

Parameters:

  • node ts.Node | undefined

Returns: node is ts.Identifier

Calls:

  • identifierIsThisKeyword
Code
export function isThisIdentifier(
  node: ts.Node | undefined,
): node is ts.Identifier {
  return (
    !!node &&
    node.kind === SyntaxKind.Identifier &&
    identifierIsThisKeyword(node as ts.Identifier)
  );
}

isThisInTypeQuery(node: ts.Node): boolean

Parameters:

  • node ts.Node

Returns: boolean

Calls:

  • isThisIdentifier
  • ts.isQualifiedName
Code
export function isThisInTypeQuery(node: ts.Node): boolean {
  if (!isThisIdentifier(node)) {
    return false;
  }

  while (ts.isQualifiedName(node.parent) && node.parent.left === node) {
    node = node.parent;
  }

  return node.parent.kind === SyntaxKind.TypeQuery;
}

isValidAssignmentTarget(node: ts.Node): boolean

Parameters:

  • node ts.Node

Returns: boolean

Calls:

  • isValidAssignmentTarget
Code
export function isValidAssignmentTarget(node: ts.Node): boolean {
  switch (node.kind) {
    case SyntaxKind.Identifier:
      return true;
    case SyntaxKind.PropertyAccessExpression:
    case SyntaxKind.ElementAccessExpression:
      if (node.flags & ts.NodeFlags.OptionalChain) {
        return false;
      }
      return true;
    case SyntaxKind.ParenthesizedExpression:
    case SyntaxKind.TypeAssertionExpression:
    case SyntaxKind.AsExpression:
    case SyntaxKind.SatisfiesExpression:
    case SyntaxKind.ExpressionWithTypeArguments:
    case SyntaxKind.NonNullExpression:
      return isValidAssignmentTarget(
        (
          node as
            | ts.AssertionExpression
            | ts.ExpressionWithTypeArguments
            | ts.NonNullExpression
            | ts.ParenthesizedExpression
            | ts.SatisfiesExpression
        ).expression,
      );
    default:
      return false;
  }
}

getNamespaceModifiers(node: ts.ModuleDeclaration): ts.Modifier[] | undefined

Parameters:

  • node ts.ModuleDeclaration

Returns: ts.Modifier[] | undefined

Calls:

  • getModifiers (from ./getModifiers)
  • ts.isModuleDeclaration

Internal Comments:

// For following nested namespaces, use modifiers given to the topmost namespace (x2)
//   export declare namespace foo.bar.baz {} (x2)

Code
export function getNamespaceModifiers(
  node: ts.ModuleDeclaration,
): ts.Modifier[] | undefined {
  // For following nested namespaces, use modifiers given to the topmost namespace
  //   export declare namespace foo.bar.baz {}
  let modifiers = getModifiers(node);
  let moduleDeclaration = node;
  while (
    (!modifiers || modifiers.length === 0) &&
    ts.isModuleDeclaration(moduleDeclaration.parent)
  ) {
    const parentModifiers = getModifiers(moduleDeclaration.parent);
    if (parentModifiers?.length) {
      modifiers = parentModifiers;
    }
    moduleDeclaration = moduleDeclaration.parent;
  }
  return modifiers;
}

declarationNameToString(node: ts.Node): string

Parameters:

  • node ts.Node

Returns: string

Calls:

  • node.getSourceFile().text.slice(node.pos, node.end).trimStart
Code
export function declarationNameToString(node: ts.Node): string {
  const text = node.getSourceFile().text.slice(node.pos, node.end).trimStart();
  return text || '(Missing)';
}

isEntityNameExpression(node: ts.Node): node is ts.EntityNameExpression

Parameters:

  • node ts.Node

Returns: node is ts.EntityNameExpression

Calls:

  • isPropertyAccessEntityNameExpression
Code
export function isEntityNameExpression(
  node: ts.Node,
): node is ts.EntityNameExpression {
  return (
    node.kind === SyntaxKind.Identifier ||
    isPropertyAccessEntityNameExpression(node)
  );
}

isAssignmentOperator(operator: ts.BinaryOperatorToken): operator is ts.Token<AssignmentOperatorKind>

Returns true if the given ts.Token is the assignment operator

Raw JSDoc
/**
 * Returns true if the given ts.Token is the assignment operator
 */

Calls:

  • (ASSIGNMENT_OPERATORS as ReadonlySet<ts.SyntaxKind>).has
Code
function isAssignmentOperator(
  operator: ts.BinaryOperatorToken,
): operator is ts.Token<AssignmentOperatorKind> {
  return (ASSIGNMENT_OPERATORS as ReadonlySet<ts.SyntaxKind>).has(
    operator.kind,
  );
}

isJSDocComment(node: ts.Node): node is ts.JSDoc

Returns true if the given ts.Node is a JSDoc comment

Raw JSDoc
/**
 * Returns true if the given ts.Node is a JSDoc comment
 */

Internal Comments:

// eslint-disable-next-line @typescript-eslint/no-deprecated -- SyntaxKind.JSDoc was only added in TS4.7 so we can't use it yet

Code
function isJSDocComment(node: ts.Node): node is ts.JSDoc {
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- SyntaxKind.JSDoc was only added in TS4.7 so we can't use it yet
  return node.kind === SyntaxKind.JSDocComment;
}

isToken(node: ts.Node): node is ts.Token<ts.TokenSyntaxKind>

Returns true if a given ts.Node is a token

Raw JSDoc
/**
 * Returns true if a given ts.Node is a token
 */
Code
function isToken(node: ts.Node): node is ts.Token<ts.TokenSyntaxKind> {
  return (
    node.kind >= SyntaxKind.FirstToken && node.kind <= SyntaxKind.LastToken
  );
}

isPropertyAccessEntityNameExpression(node: ts.Node): node is ts.PropertyAccessEntityNameExpression

Parameters:

  • node ts.Node

Returns: node is ts.PropertyAccessEntityNameExpression

Calls:

  • ts.isPropertyAccessExpression
  • ts.isIdentifier
  • isEntityNameExpression
Code
function isPropertyAccessEntityNameExpression(
  node: ts.Node,
): node is ts.PropertyAccessEntityNameExpression {
  return (
    ts.isPropertyAccessExpression(node) &&
    ts.isIdentifier(node.name) &&
    isEntityNameExpression(node.expression)
  );
}

Internal helpers

Declared inside another function in this file.

find(n: ts.Node): ts.Node | undefined

Parameters:

  • n ts.Node

Returns: ts.Node | undefined

Calls:

  • ts.isToken
  • firstDefined
  • n.getChildren
  • nodeHasTokens
  • find

Internal Comments:

// this is token that starts at the end of previous token - return it
// previous token is enclosed somewhere in the child (x2)
// previous token ends exactly at the beginning of child (x3)

Code
function find(n: ts.Node): ts.Node | undefined {
    if (ts.isToken(n) && n.pos === previousToken.end) {
      // this is token that starts at the end of previous token - return it
      return n;
    }
    return firstDefined(n.getChildren(ast), (child: ts.Node) => {
      const shouldDiveInChildNode =
        // previous token is enclosed somewhere in the child
        (child.pos <= previousToken.pos && child.end > previousToken.end) ||
        // previous token ends exactly at the beginning of child
        child.pos === previousToken.end;
      return shouldDiveInChildNode && nodeHasTokens(child, ast)
        ? find(child)
        : undefined;
    });
  }

walk(node: ts.Node): void

Parameters:

  • node any: the ts.Node
Raw JSDoc
/**
   * @param node the ts.Node
   */

Calls:

  • isComment
  • isJSDocComment
  • isToken
  • result.push
  • convertToken
  • node.getChildren(ast).forEach

Internal Comments:

// TypeScript generates tokens for types in JSDoc blocks. Comment tokens
// and their children should not be walked or added to the resulting tokens list.

Code
function walk(node: ts.Node): void {
    // TypeScript generates tokens for types in JSDoc blocks. Comment tokens
    // and their children should not be walked or added to the resulting tokens list.
    if (isComment(node) || isJSDocComment(node)) {
      return;
    }

    if (isToken(node) && node.kind !== SyntaxKind.EndOfFileToken) {
      result.push(convertToken(node, ast));
    } else {
      node.getChildren(ast).forEach(walk);
    }
  }

Classes

TSError

Extends: Error

Class Code
export class TSError extends Error {
  override name = 'TSError';

  constructor(
    message: string,
    public readonly fileName: string,
    public readonly location: {
      end: {
        column: number;
        line: number;
        offset: number;
      };
      start: {
        column: number;
        line: number;
        offset: number;
      };
    },
  ) {
    super(message);
  }

  // For old version of ESLint https://github.com/typescript-eslint/typescript-eslint/pull/6556#discussion_r1123237311
  get index(): number {
    return this.location.start.offset;
  }

  // https://github.com/eslint/eslint/blob/b09a512107249a4eb19ef5a37b0bd672266eafdb/lib/linter/linter.js#L853
  get lineNumber(): number {
    return this.location.start.line;
  }

  // https://github.com/eslint/eslint/blob/b09a512107249a4eb19ef5a37b0bd672266eafdb/lib/linter/linter.js#L854
  get column(): number {
    return this.location.start.column;
  }
}

Interfaces

TokenToText

Interface Code
interface TokenToText
  extends TSESTree.PunctuatorTokenToText, TSESTree.BinaryOperatorToText {
  [SyntaxKind.ImportKeyword]: 'import';
  [SyntaxKind.KeyOfKeyword]: 'keyof';
  [SyntaxKind.NewKeyword]: 'new';
  [SyntaxKind.ReadonlyKeyword]: 'readonly';
  [SyntaxKind.UniqueKeyword]: 'unique';
}

Properties

Name Type Optional Description
[SyntaxKind.ImportKeyword] 'import' not shown
[SyntaxKind.KeyOfKeyword] 'keyof' not shown
[SyntaxKind.NewKeyword] 'new' not shown
[SyntaxKind.ReadonlyKeyword] 'readonly' not shown
[SyntaxKind.UniqueKeyword] 'unique' not shown

Type Aliases

LogicalOperatorKind

type LogicalOperatorKind = | ts.SyntaxKind.AmpersandAmpersandToken
  | ts.SyntaxKind.BarBarToken
  | ts.SyntaxKind.QuestionQuestionToken;

AssignmentOperatorKind

type AssignmentOperatorKind = keyof TSESTree.AssignmentOperatorToText;

BinaryOperatorKind

type BinaryOperatorKind = keyof TSESTree.BinaryOperatorToText;

DeclarationKind

type DeclarationKind = TSESTree.VariableDeclaration['kind'];

TokenForTokenKind<T extends ts.SyntaxKind>

type TokenForTokenKind<T extends ts.SyntaxKind> = T extends keyof TokenToText
  ? TokenToText[T]
  : string | undefined;

Generated by Syntax Scribe