Utility functions for working with TypeScript's API, providing comprehensive tools for analyzing and manipulating TypeScript AST nodes, types, and compiler APIs.
npx @tessl/cli install tessl/npm-ts-api-utils@2.0.0A 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).
ts-api-utilsnpm install ts-api-utilsThe 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");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`);
}
}ts-api-utils is organized into logical modules that mirror different aspects of TypeScript analysis:
isBlockStatement)getAccessKind)forEachToken)collectVariableUsage)The library is structured around these main capability areas:
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;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;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;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;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;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;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;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;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;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;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;ts-api-utils works best when combined with:
collectVariableUsagecollectVariableUsage is the most expensive operation; use judiciously for large filesforEachToken can be expensive for very large nodes; consider targeted analysis insteadts-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.