Utility functions for working with TypeScript's Abstract Syntax Tree (AST) and type system
—
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.
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;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;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;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;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;
}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;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>;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:
Install with Tessl CLI
npx tessl i tessl/npm-tsutils