or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

compiler-options.mdindex.mdnode-analysis.mdsyntax-utilities.mdtype-system.mdusage-analysis.md
tile.json

tessl/npm-ts-api-utils

Utility functions for working with TypeScript's API, providing comprehensive tools for analyzing and manipulating TypeScript AST nodes, types, and compiler APIs.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/ts-api-utils@2.0.x

To install, run

npx @tessl/cli install tessl/npm-ts-api-utils@2.0.0

index.mddocs/

ts-api-utils

A comprehensive collection of utility functions for working with TypeScript's API. This library serves as the successor to the popular tsutils library, providing a modern, well-typed toolkit for building TypeScript tooling, linters, code analyzers, and other developer tools that need to work with TypeScript's Abstract Syntax Tree (AST).

Package Information

  • Package Name: ts-api-utils
  • Version: 2.0.0
  • Language: TypeScript (compiled to JavaScript)
  • License: MIT
  • Repository: github.com/JoshuaKGoldberg/ts-api-utils
  • Minimum TypeScript Version: 4.8.4
  • Node Version: >=18.12

Installation

npm install ts-api-utils

Core Imports

The library supports both ESM and CommonJS imports:

// ESM import (recommended)
import * as tsutils from "ts-api-utils";

// Named imports
import { 
  forEachToken, 
  isBlockStatement, 
  getCallSignaturesOfType,
  collectVariableUsage,
  isTsVersionAtLeast 
} from "ts-api-utils";

// CommonJS (legacy)
const tsutils = require("ts-api-utils");

Basic Usage

ts-api-utils provides utilities that work seamlessly with TypeScript's compiler API:

import * as ts from "typescript";
import { forEachToken, isBlockStatement, getCallSignaturesOfType } from "ts-api-utils";

// Example: Iterate over all tokens in a node
function analyzeNode(node: ts.Node, sourceFile: ts.SourceFile) {
  forEachToken(node, (token) => {
    console.log(`Token: ${ts.SyntaxKind[token.kind]}`);
  }, sourceFile);
}

// Example: Type-safe node checking
function processStatement(node: ts.Node) {
  if (isBlockStatement(node)) {
    // TypeScript now knows 'node' is a BlockStatement
    console.log(`Block has ${node.statements.length} statements`);
  }
}

// Example: Working with type information
function analyzeType(type: ts.Type) {
  const signatures = getCallSignaturesOfType(type);
  if (signatures.length > 0) {
    console.log(`Type has ${signatures.length} call signatures`);
  }
}

Architecture

ts-api-utils is organized into logical modules that mirror different aspects of TypeScript analysis:

Core Design Patterns

  1. Type Guards: Functions that narrow TypeScript types (e.g., isBlockStatement)
  2. Analyzers: Functions that extract information from AST nodes (e.g., getAccessKind)
  3. Utilities: Helper functions for common operations (e.g., forEachToken)
  4. Collectors: Functions that gather data across larger code structures (e.g., collectVariableUsage)

Module Organization

The library is structured around these main capability areas:

  • Node Analysis: The largest module, containing type guards for every TypeScript node type
  • Type System Utilities: Second largest module, providing deep integration with TypeScript's type system
  • Usage Analysis: Advanced scope and variable tracking capabilities
  • Token Processing: Low-level token iteration and manipulation
  • Syntax Utilities: Common syntax analysis patterns
  • Compiler Integration: Working with compiler options and flags
  • Comment Processing: Extracting and working with code comments
  • Scope Analysis: Understanding scope boundaries and relationships
  • Modifier Utilities: Working with TypeScript modifiers

Capabilities

Comments Processing

Working with TypeScript comments attached to AST nodes.

When to use: Extract documentation, analyze comment patterns, or build tools that need to preserve or manipulate comments during code transformations.

import { forEachComment, ForEachCommentCallback } from "ts-api-utils";

// Callback for processing each comment
type ForEachCommentCallback = (fullText: string, comment: ts.CommentRange) => void;

// Iterate over all comments owned by a node or its children
function forEachComment(
  node: ts.Node, 
  callback: ForEachCommentCallback, 
  sourceFile?: ts.SourceFile
): void;

Syntax Utilities Guide

Compiler Options

Analyzing and testing TypeScript compiler configuration settings.

When to use: Build tools that need to understand compiler settings, validate configurations, or provide different behavior based on strict mode settings.

import { 
  isCompilerOptionEnabled, 
  isStrictCompilerOptionEnabled,
  BooleanCompilerOptions,
  StrictCompilerOption 
} from "ts-api-utils";

// Options that can be tested with isCompilerOptionEnabled
type BooleanCompilerOptions = keyof {
  [K in keyof ts.CompilerOptions as NonNullable<ts.CompilerOptions[K]> extends boolean ? K : never]: unknown;
};

// Strict mode options
type StrictCompilerOption = 
  | "alwaysStrict" 
  | "noImplicitAny" 
  | "noImplicitThis" 
  | "strictBindCallApply" 
  | "strictFunctionTypes" 
  | "strictNullChecks" 
  | "strictPropertyInitialization";

// Check if a compiler option is enabled (handles option dependencies)
function isCompilerOptionEnabled(
  options: ts.CompilerOptions, 
  option: BooleanCompilerOptions
): boolean;

// Check strict mode options (accounts for global strict setting)
function isStrictCompilerOptionEnabled(
  options: ts.CompilerOptions, 
  option: StrictCompilerOption
): boolean;

Compiler Options Guide

Flag Utilities

Testing various TypeScript flags on nodes, types, and symbols.

When to use: Determine node characteristics, check type properties, or analyze symbol metadata in static analysis tools.

import { 
  isModifierFlagSet, 
  isNodeFlagSet, 
  isObjectFlagSet, 
  isSymbolFlagSet, 
  isTypeFlagSet 
} from "ts-api-utils";

// Test modifier flags on declarations
function isModifierFlagSet(node: ts.Declaration, flag: ts.ModifierFlags): boolean;

// Test node flags
function isNodeFlagSet(node: ts.Node, flag: ts.NodeFlags): boolean;

// Test object type flags
function isObjectFlagSet(objectType: ts.ObjectType, flag: ts.ObjectFlags): boolean;

// Test symbol flags
function isSymbolFlagSet(symbol: ts.Symbol, flag: ts.SymbolFlags): boolean;

// Test type flags
function isTypeFlagSet(type: ts.Type, flag: ts.TypeFlags): boolean;

Flag Utilities Guide

Modifier Utilities

Working with TypeScript modifier tokens like public, private, static, etc.

When to use: Analyze access modifiers, check for specific modifiers, or build tools that need to understand declaration visibility.

import { includesModifier } from "ts-api-utils";

// Test if modifiers include any of the given kinds
function includesModifier(
  modifiers: Iterable<ts.ModifierLike> | undefined, 
  ...kinds: ts.ModifierSyntaxKind[]
): boolean;

Modifier Utilities Guide

Node Analysis

The most comprehensive module providing type guards and utilities for every TypeScript AST node type.

When to use: This is the core of most TypeScript analysis tools. Use these functions whenever you need to safely narrow node types, check node properties, or traverse the AST with type safety.

import { 
  isBlockStatement,
  isVariableDeclaration,
  isIterationStatement,
  isNamedDeclarationWithName,
  isConstAssertionExpression,
  hasModifiers,
  hasType,
  isAccessExpression,
  getAccessKind,
  AccessKind
} from "ts-api-utils";

// Access patterns for expressions
enum AccessKind {
  None = 0,
  Read = 1,
  Write = 2,
  Delete = 4,
  ReadWrite = 3
}

// Type guards for compound node types
interface NamedDeclarationWithName extends ts.NamedDeclaration {
  readonly name: ts.DeclarationName;
}

interface ConstAssertionExpression extends ts.AssertionExpression {
  readonly type: ts.TypeNode & { readonly typeName: ConstAssertionIdentifier };
}

// Essential node type guards
function isBlockStatement(node: ts.Node): node is ts.BlockStatement;
function isVariableDeclaration(node: ts.Node): node is ts.VariableDeclaration;
function isIterationStatement(node: ts.Node): node is ts.IterationStatement;
function isNamedDeclarationWithName(node: ts.Declaration): node is NamedDeclarationWithName;

// Property-based type guards
function hasModifiers(node: ts.Node): node is ts.HasModifiers;
function hasType(node: ts.Node): node is ts.HasType;
function isAccessExpression(node: ts.Node): node is ts.AccessExpression;

// Expression analysis
function getAccessKind(node: ts.Expression): AccessKind;

Node Analysis Guide

Scope Analysis

Determining scope boundaries and understanding lexical scoping in TypeScript.

When to use: Build variable tracking tools, analyze closure behavior, or implement scope-aware refactoring tools.

import { isFunctionScopeBoundary } from "ts-api-utils";

// Check if a node creates a function scope boundary
function isFunctionScopeBoundary(node: ts.Node): boolean;

Scope Analysis Guide

Syntax Utilities

General utilities for common syntax analysis patterns.

When to use: Validate identifiers, check assignment patterns, or analyze property access expressions.

import { 
  isAssignmentKind, 
  isNumericPropertyName, 
  isValidPropertyAccess 
} from "ts-api-utils";

// Test if a syntax kind represents assignment
function isAssignmentKind(kind: ts.SyntaxKind): boolean;

// Test if a string is a numeric property name
function isNumericPropertyName(name: string | ts.__String): boolean;

// Check if text can be used in property access expressions
function isValidPropertyAccess(text: string, languageVersion?: ts.ScriptTarget): boolean;

Syntax Utilities Guide

Token Processing

Low-level iteration and manipulation of tokens in TypeScript source code.

When to use: Build formatters, implement custom syntax highlighting, or perform detailed source code analysis that needs to examine every token.

import { forEachToken, ForEachTokenCallback } from "ts-api-utils";

// Callback for processing each token
type ForEachTokenCallback = (token: ts.Node) => void;

// Iterate over all tokens of a node
function forEachToken(
  node: ts.Node, 
  callback: ForEachTokenCallback, 
  sourceFile?: ts.SourceFile
): void;

Token Processing Guide

Type System Utilities

Comprehensive utilities for working with TypeScript's type system, including type analysis, type guards, and type manipulation.

When to use: Build type-aware linting rules, analyze type relationships, implement type-based refactoring tools, or create tools that need deep integration with TypeScript's type checker.

import { 
  getCallSignaturesOfType,
  getPropertyOfType,
  intersectionTypeParts,
  unionTypeParts,
  typeParts,
  isFalsyType,
  isThenableType,
  typeIsLiteral,
  isPropertyReadonlyInType,
  symbolHasReadonlyDeclaration,
  isConditionalType,
  isEnumType,
  isIndexedAccessType,
  isIntersectionType,
  isUnionType,
  isObjectType,
  isTupleType,
  isTypeReference,
  isBooleanLiteralType,
  isBigIntLiteralType,
  isStringLiteralType,
  isNumberLiteralType
} from "ts-api-utils";

// Type information retrieval
function getCallSignaturesOfType(type: ts.Type): readonly ts.Signature[];
function getPropertyOfType(type: ts.Type, name: ts.__String): ts.Symbol | undefined;

// Type decomposition utilities
function intersectionTypeParts(type: ts.Type): ts.Type[];
function unionTypeParts(type: ts.Type): ts.Type[];
function typeParts(type: ts.Type): ts.Type[];

// Type analysis utilities  
function isFalsyType(type: ts.Type): boolean;
function isThenableType(typeChecker: ts.TypeChecker, node: ts.Node, type: ts.Type): boolean;
function typeIsLiteral(type: ts.Type): type is ts.LiteralType;
function isPropertyReadonlyInType(
  type: ts.Type, 
  name: ts.__String, 
  typeChecker: ts.TypeChecker
): boolean;

// Symbol utilities
function symbolHasReadonlyDeclaration(symbol: ts.Symbol, typeChecker: ts.TypeChecker): boolean;

// Core type guards
function isConditionalType(type: ts.Type): type is ts.ConditionalType;
function isEnumType(type: ts.Type): type is ts.EnumType;
function isIndexedAccessType(type: ts.Type): type is ts.IndexedAccessType;
function isIntersectionType(type: ts.Type): type is ts.IntersectionType;
function isUnionType(type: ts.Type): type is ts.UnionType;
function isObjectType(type: ts.Type): type is ts.ObjectType;

// Object type utilities
function isTupleType(type: ts.Type): type is ts.TupleType;
function isTypeReference(type: ts.Type): type is ts.TypeReference;

// Literal type guards
function isBooleanLiteralType(type: ts.Type): type is ts.LiteralType;
function isBigIntLiteralType(type: ts.Type): type is ts.BigIntLiteralType;
function isStringLiteralType(type: ts.Type): type is ts.StringLiteralType;
function isNumberLiteralType(type: ts.Type): type is ts.NumberLiteralType;

Type System Guide

Usage Analysis

Advanced analysis of variable and symbol usage patterns, including scope tracking and declaration analysis.

When to use: Build unused variable detectors, implement variable renaming tools, analyze variable lifecycle, or create tools that need comprehensive understanding of identifier usage across scopes.

import { 
  collectVariableUsage, 
  UsageInfo, 
  Usage, 
  DeclarationDomain, 
  UsageDomain,
  isBlockScopeBoundary,
  ScopeBoundary,
  getDeclarationDomain,
  getUsageDomain,
  identifierToKeywordKind
} from "ts-api-utils";

// Usage domains (type vs value space)
enum DeclarationDomain {
  Namespace = 1,
  Type = 2, 
  Value = 4,
  Any = 7,
  Import = 8
}

enum UsageDomain {
  Namespace = 1,
  Type = 2,
  Value = 4, 
  Any = 7,
  TypeQuery = 8,
  ValueOrNamespace = 5
}

// Usage tracking interfaces
interface Usage {
  readonly domain: UsageDomain;
  readonly location: ts.Identifier;
}

interface UsageInfo {
  readonly declarations: ts.Identifier[];
  readonly domain: DeclarationDomain;
  readonly exported: boolean;
  readonly inGlobalScope: boolean;
  readonly uses: Usage[];
}

// Scope boundary analysis
enum ScopeBoundary {
  None = 0,
  Function = 1,
  Block = 2,
  Type = 4,
  ConditionalType = 8
}

// Main usage collection function
function collectVariableUsage(sourceFile: ts.SourceFile): Map<ts.Identifier, UsageInfo>;

// Scope and domain analysis functions
function isBlockScopeBoundary(node: ts.Node): ScopeBoundary;
function getDeclarationDomain(node: ts.Identifier): DeclarationDomain | undefined;
function getUsageDomain(node: ts.Identifier): UsageDomain | undefined;

// TypeScript version compatibility utility
function identifierToKeywordKind(node: ts.Identifier): ts.SyntaxKind | undefined;

Usage Analysis Guide

Utility Functions

General utility functions for TypeScript version compatibility and common operations.

When to use: Check TypeScript version compatibility for feature detection, or perform version-specific operations.

import { isTsVersionAtLeast } from "ts-api-utils";

// Check if the current TypeScript version is at least the specified version
function isTsVersionAtLeast(major: number, minor?: number): boolean;

Related Libraries

ts-api-utils works best when combined with:

  • TypeScript Compiler API: The foundation that ts-api-utils extends
  • @typescript-eslint/utils: For building ESLint rules with TypeScript support
  • ts-morph: Higher-level TypeScript manipulation library
  • typescript: The TypeScript compiler itself (peer dependency)

Best Practices

  1. Always use type guards: The library's type guards provide essential type safety when working with AST nodes
  2. Leverage the type system: Functions are designed to work seamlessly with TypeScript's type inference
  3. Combine utilities: Most real-world use cases benefit from combining multiple utilities (e.g., node analysis + type checking + usage analysis)
  4. Cache results: For performance-critical applications, consider caching results of expensive operations like collectVariableUsage
  5. Use with source files: Many functions work better when provided with the source file context

Performance Considerations

  • Usage analysis: collectVariableUsage is the most expensive operation; use judiciously for large files
  • Type operations: Type-related utilities require a TypeChecker instance; reuse when possible
  • Token iteration: forEachToken can be expensive for very large nodes; consider targeted analysis instead
  • Scope analysis: Scope boundary detection is generally fast and safe to use liberally

ts-api-utils provides the essential building blocks for any serious TypeScript tooling project, offering both low-level token manipulation and high-level semantic analysis capabilities.