CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tsutils

Utility functions for working with TypeScript's Abstract Syntax Tree (AST) and type system

Pending
Overview
Eval results
Files

type-utilities.mddocs/

Type System Utilities

Advanced type system analysis including type assignability checking, property analysis, class type inspection, type manipulation utilities, and comprehensive type system operations for deep TypeScript compiler integration.

Capabilities

Type Assignability Analysis

Utilities for checking type compatibility and assignability relationships.

/**
 * Check if type is assignable to number
 * @param checker - Type checker instance
 * @param type - Type to check
 * @returns true if type is assignable to number
 */
function isTypeAssignableToNumber(checker: ts.TypeChecker, type: ts.Type): boolean;

/**
 * Check if type is assignable to string
 * @param checker - Type checker instance
 * @param type - Type to check
 * @returns true if type is assignable to string
 */
function isTypeAssignableToString(checker: ts.TypeChecker, type: ts.Type): boolean;

/**
 * Check if type is thenable (has then method)
 * @param checker - Type checker instance
 * @param node - Node for context
 * @param type - Type to check
 * @returns true if type is thenable (Promise-like)
 */
function isThenableType(checker: ts.TypeChecker, node: ts.Node, type: ts.Type): boolean;

/**
 * Check if type is thenable based on expression
 * @param checker - Type checker instance
 * @param node - Expression node
 * @param type - Optional type (inferred from node if not provided)
 * @returns true if type is thenable
 */
function isThenableType(checker: ts.TypeChecker, node: ts.Expression, type?: ts.Type): boolean;

Type Classification

Utilities for classifying and analyzing type characteristics.

/**
 * Check if type is falsy (null, undefined, false, 0, "", etc.)
 * @param type - Type to check
 * @returns true if type is always falsy
 */
function isFalsyType(type: ts.Type): boolean;

/**
 * Check if type is specific boolean literal type
 * @param type - Type to check
 * @param literal - Boolean value to match
 * @returns true if type is the specified boolean literal
 */
function isBooleanLiteralType(type: ts.Type, literal: boolean): boolean;

/**
 * Check if object type is empty (no properties)
 * @param type - Type to check
 * @returns true if type is empty object type
 */
function isEmptyObjectType(type: ts.Type): type is ts.ObjectType;

Optional Chaining and Optionality

Utilities for handling optional chaining and optional types.

/**
 * Remove optionality from type (T | undefined -> T)
 * @param checker - Type checker instance
 * @param type - Type to process
 * @returns Type with optionality removed
 */
function removeOptionalityFromType(checker: ts.TypeChecker, type: ts.Type): ts.Type;

/**
 * Remove optional chaining undefined marker type
 * @param checker - Type checker instance
 * @param type - Type to process
 * @returns Type with optional chaining marker removed
 */
function removeOptionalChainingUndefinedMarkerType(checker: ts.TypeChecker, type: ts.Type): ts.Type;

/**
 * Check if type is optional chaining undefined marker
 * @param checker - Type checker instance
 * @param type - Type to check
 * @returns true if type is optional chaining marker
 */
function isOptionalChainingUndefinedMarkerType(checker: ts.TypeChecker, type: ts.Type): boolean;

Union and Intersection Type Analysis

Utilities for working with composite types.

/**
 * Get constituent types of a union type
 * @param type - Union type to decompose
 * @returns Array of union member types
 */
function unionTypeParts(type: ts.Type): ts.Type[];

/**
 * Get constituent types of an intersection type
 * @param type - Intersection type to decompose
 * @returns Array of intersection member types
 */
function intersectionTypeParts(type: ts.Type): ts.Type[];

/**
 * Test some parts of a union or intersection type
 * @param type - Type to test
 * @param predicate - Type predicate function
 * @param cb - Callback to test each part
 * @returns true if callback returns true for any part
 */
function someTypePart(
  type: ts.Type, 
  predicate: (t: ts.Type) => t is ts.UnionOrIntersectionType, 
  cb: (t: ts.Type) => boolean
): boolean;

Property Analysis

Utilities for analyzing type properties and symbols.

/**
 * Get property symbol from type by name
 * @param type - Type to search
 * @param name - Property name to find
 * @returns Property symbol if found
 */
function getPropertyOfType(type: ts.Type, name: ts.__String): ts.Symbol | undefined;

/**
 * Get well-known symbol property from type
 * @param type - Type to search
 * @param wellKnownSymbolName - Well-known symbol name (e.g., 'iterator')
 * @param checker - Type checker instance
 * @returns Property symbol if found
 */
function getWellKnownSymbolPropertyOfType(
  type: ts.Type, 
  wellKnownSymbolName: string, 
  checker: ts.TypeChecker
): ts.Symbol | undefined;

/**
 * Check if property is readonly in type
 * @param type - Type containing property
 * @param name - Property name
 * @param checker - Type checker instance
 * @returns true if property is readonly
 */
function isPropertyReadonlyInType(
  type: ts.Type, 
  name: ts.__String, 
  checker: ts.TypeChecker
): boolean;

/**
 * Check if symbol has readonly declaration
 * @param symbol - Symbol to check
 * @param checker - Type checker instance
 * @returns true if symbol is declared readonly
 */
function symbolHasReadonlyDeclaration(symbol: ts.Symbol, checker: ts.TypeChecker): boolean;

/**
 * Get property name from type (for computed properties)
 * @param type - Type representing property name
 * @returns Property name information if extractable
 */
function getPropertyNameFromType(type: ts.Type): PropertyName | undefined;

/**
 * Property name information interface
 */
interface PropertyName {
  displayName: string;
  symbolName: ts.__String;
}

Class Type Analysis

Specialized utilities for analyzing class types and inheritance.

/**
 * Get symbol of class-like declaration
 * @param node - Class-like declaration
 * @param checker - Type checker instance
 * @returns Class symbol
 */
function getSymbolOfClassLikeDeclaration(
  node: ts.ClassLikeDeclaration, 
  checker: ts.TypeChecker
): ts.Symbol;

/**
 * Get constructor type of class-like declaration
 * @param node - Class-like declaration
 * @param checker - Type checker instance
 * @returns Constructor type
 */
function getConstructorTypeOfClassLikeDeclaration(
  node: ts.ClassLikeDeclaration, 
  checker: ts.TypeChecker
): ts.Type;

/**
 * Get instance type of class-like declaration
 * @param node - Class-like declaration
 * @param checker - Type checker instance
 * @returns Instance type
 */
function getInstanceTypeOfClassLikeDeclaration(
  node: ts.ClassLikeDeclaration, 
  checker: ts.TypeChecker
): ts.Type;

/**
 * Get base class member symbol for class element
 * @param node - Class member declaration
 * @param checker - Type checker instance
 * @returns Base class member symbol if found
 */
function getBaseClassMemberOfClassElement(
  node: ts.PropertyDeclaration | ts.MethodDeclaration | ts.AccessorDeclaration, 
  checker: ts.TypeChecker
): ts.Symbol | undefined;

Signature Analysis

Utilities for analyzing function and method signatures.

/**
 * Get call signatures from type
 * @param type - Type to analyze
 * @returns Array of call signatures
 */
function getCallSignaturesOfType(type: ts.Type): ReadonlyArray<ts.Signature>;

Iterator Type Analysis

Utilities for analyzing iterator types and async iterators.

/**
 * Extract yield result type from iterator result type
 * @param type - Iterator result type
 * @param node - Node for context
 * @param checker - Type checker instance
 * @returns Yield result type
 */
function getIteratorYieldResultFromIteratorResult(
  type: ts.Type, 
  node: ts.Node, 
  checker: ts.TypeChecker
): ts.Type;

Usage Examples:

import * as ts from "typescript";
import { 
  isTypeAssignableToNumber,
  isTypeAssignableToString,
  isFalsyType,
  unionTypeParts,
  getPropertyOfType,
  getSymbolOfClassLikeDeclaration,
  getCallSignaturesOfType,
  isThenableType
} from "tsutils/util";

// Type assignability analysis
function analyzeTypeCompatibility(checker: ts.TypeChecker, type: ts.Type) {
  if (isTypeAssignableToNumber(checker, type)) {
    console.log("Type is assignable to number");
  }
  
  if (isTypeAssignableToString(checker, type)) {
    console.log("Type is assignable to string");
  }
  
  if (isFalsyType(type)) {
    console.log("Type is always falsy");
  }
}

// Union type analysis
function analyzeUnionType(checker: ts.TypeChecker, type: ts.Type) {
  if (type.flags & ts.TypeFlags.Union) {
    const parts = unionTypeParts(type);
    console.log(`Union has ${parts.length} parts:`);
    
    parts.forEach((part, index) => {
      console.log(`  ${index}: ${checker.typeToString(part)}`);
      
      if (isTypeAssignableToString(checker, part)) {
        console.log(`    Part ${index} is string-like`);
      }
    });
  }
}

// Property analysis
function analyzeTypeProperties(checker: ts.TypeChecker, type: ts.Type) {
  if (type.symbol && type.symbol.members) {
    console.log("Type properties:");
    
    type.symbol.members.forEach((symbol, name) => {
      const property = getPropertyOfType(type, name);
      if (property) {
        console.log(`  ${name}: ${checker.typeToString(checker.getTypeOfSymbolAtLocation(property, property.valueDeclaration!))}`);
        
        if (isPropertyReadonlyInType(type, name, checker)) {
          console.log(`    (readonly)`);
        }
      }
    });
  }
}

// Class analysis
function analyzeClass(checker: ts.TypeChecker, classDecl: ts.ClassDeclaration) {
  const symbol = getSymbolOfClassLikeDeclaration(classDecl, checker);
  const constructorType = getConstructorTypeOfClassLikeDeclaration(classDecl, checker);
  const instanceType = getInstanceTypeOfClassLikeDeclaration(classDecl, checker);
  
  console.log(`Class: ${symbol.name}`);
  console.log(`Constructor type: ${checker.typeToString(constructorType)}`);
  console.log(`Instance type: ${checker.typeToString(instanceType)}`);
  
  // Analyze class members
  classDecl.members.forEach(member => {
    if (ts.isPropertyDeclaration(member) || ts.isMethodDeclaration(member) || ts.isGetAccessorDeclaration(member)) {
      const baseSymbol = getBaseClassMemberOfClassElement(member, checker);
      if (baseSymbol) {
        console.log(`Member ${member.name?.getText()} overrides base class member`);
      }
    }
  });
}

// Function type analysis
function analyzeFunctionType(checker: ts.TypeChecker, type: ts.Type) {
  const signatures = getCallSignaturesOfType(type);
  
  if (signatures.length > 0) {
    console.log(`Function has ${signatures.length} call signature(s):`);
    
    signatures.forEach((sig, index) => {
      const sigString = checker.signatureToString(sig);
      console.log(`  ${index}: ${sigString}`);
      
      const returnType = sig.getReturnType();
      if (isThenableType(checker, sig.declaration!, returnType)) {
        console.log(`    Returns Promise-like type`);
      }
    });
  }
}

// Promise/thenable analysis
function analyzeAsyncTypes(checker: ts.TypeChecker, node: ts.Expression) {
  const type = checker.getTypeAtLocation(node);
  
  if (isThenableType(checker, node, type)) {
    console.log("Expression is thenable (Promise-like)");
    
    // Try to get the resolved type
    const thenProperty = getPropertyOfType(type, 'then' as ts.__String);
    if (thenProperty) {
      const thenType = checker.getTypeOfSymbolAtLocation(thenProperty, node);
      console.log(`Then method type: ${checker.typeToString(thenType)}`);
    }
  }
}

// Complex type analysis combining multiple utilities
function performDeepTypeAnalysis(checker: ts.TypeChecker, type: ts.Type, node: ts.Node) {
  console.log(`\nAnalyzing type: ${checker.typeToString(type)}`);
  
  // Basic classification
  if (isFalsyType(type)) {
    console.log("  Type is falsy");
  }
  
  if (isEmptyObjectType(type)) {
    console.log("  Type is empty object");
  }
  
  // Composite type analysis
  if (type.flags & ts.TypeFlags.Union) {
    const parts = unionTypeParts(type);
    console.log(`  Union with ${parts.length} parts`);
    
    const stringParts = parts.filter(p => isTypeAssignableToString(checker, p));
    const numberParts = parts.filter(p => isTypeAssignableToNumber(checker, p));
    
    console.log(`    String-assignable parts: ${stringParts.length}`);
    console.log(`    Number-assignable parts: ${numberParts.length}`);
  }
  
  // Callable analysis
  const signatures = getCallSignaturesOfType(type);
  if (signatures.length > 0) {
    console.log(`  Callable with ${signatures.length} signature(s)`);
    
    const asyncSignatures = signatures.filter(sig => {
      const returnType = sig.getReturnType();
      return isThenableType(checker, node, returnType);
    });
    
    if (asyncSignatures.length > 0) {
      console.log(`    ${asyncSignatures.length} async signature(s)`);
    }
  }
}

Advanced Use Cases:

  1. Type-aware Refactoring: Understanding type relationships for safe code transformations
  2. API Compatibility Checking: Verifying type compatibility across API versions
  3. Generic Type Resolution: Working with complex generic types and constraints
  4. Promise/Async Analysis: Detecting and analyzing asynchronous code patterns
  5. Class Hierarchy Analysis: Understanding inheritance and member overrides
  6. Property Mutation Detection: Identifying readonly vs mutable properties
  7. Type Narrowing: Building sophisticated type guards and narrowing logic
  8. Iterator Pattern Analysis: Working with iterables and async iterables

Install with Tessl CLI

npx tessl i tessl/npm-tsutils

docs

ast-traversal.md

code-analysis.md

control-flow.md

index.md

node-typeguards.md

text-processing.md

type-guards.md

type-utilities.md

variable-usage.md

tile.json