Utility functions for working with TypeScript's API, providing comprehensive tools for analyzing and manipulating TypeScript AST nodes, types, and compiler APIs.
79
The Type System Utilities are the second largest collection of functions in ts-api-utils, providing comprehensive tools for analyzing, inspecting, and working with TypeScript's type system. These utilities are essential for building robust TypeScript tooling, linters, and code analysis tools that need to understand and manipulate type information.
TypeScript's type system is complex and hierarchical, with various type categories including intrinsic types (built-in primitives), literal types, object types, union/intersection types, and more specialized constructs. The type system utilities provide a systematic way to inspect and classify types, extract type information, and perform type-aware operations.
TypeScript represents types as instances of the ts.Type interface. Each type has flags (ts.TypeFlags) that indicate its category and characteristics. Types can be simple primitives like string or number, complex object types, or computed types like unions and intersections.
The TypeScript compiler provides a TypeChecker that can resolve types at specific locations in the AST, compare types, and provide detailed type information. Many utilities work in conjunction with the type checker to provide accurate analysis.
Type guards are functions that narrow the type of a value through runtime checks, allowing TypeScript to understand more specific type information in conditional blocks. The utilities provide numerous type guards for different type categories.
Functions that extract or derive type information from TypeScript types.
Get the call signatures of a given type, handling union and intersection types appropriately.
function getCallSignaturesOfType(type: ts.Type): readonly ts.Signature[]Parameters:
type: ts.Type - The type to get call signatures fromReturns: Array of call signatures. For union types, returns all signatures from all union members. For intersection types, returns signatures only if exactly one member has signatures.
Example:
declare const type: ts.Type;
const signatures = getCallSignaturesOfType(type);
for (const signature of signatures) {
// Process each call signature
}Get a specific property symbol from a type by name, including support for well-known symbols.
function getPropertyOfType(type: ts.Type, name: ts.__String): ts.Symbol | undefinedParameters:
type: ts.Type - The type to search for the propertyname: ts.__String - The escaped property name to look forReturns: The symbol representing the property, or undefined if not found
Example:
declare const property: ts.Symbol;
declare const type: ts.Type;
const symbol = getPropertyOfType(type, property.getEscapedName());
if (symbol) {
// Property exists on the type
}Retrieve a type symbol corresponding to a well-known symbol name (like Symbol.iterator).
function getWellKnownSymbolPropertyOfType(
type: ts.Type,
wellKnownSymbolName: string,
typeChecker: ts.TypeChecker
): ts.Symbol | undefinedParameters:
type: ts.Type - The type to search inwellKnownSymbolName: string - The well-known symbol name (e.g., "asyncIterator", "iterator")typeChecker: ts.TypeChecker - The type checker for resolving symbolsReturns: The symbol for the well-known symbol property, or undefined if not found
Example:
declare const type: ts.Type;
declare const typeChecker: ts.TypeChecker;
const asyncIteratorSymbol = getWellKnownSymbolPropertyOfType(
type,
"asyncIterator",
typeChecker
);Functions that check for TypeScript's built-in intrinsic types.
interface IntrinsicType extends ts.Type {
intrinsicName: string;
objectFlags: ts.ObjectFlags;
}
interface IntrinsicAnyType extends IntrinsicType {
intrinsicName: "any";
}
interface IntrinsicBigIntType extends IntrinsicType {
intrinsicName: "bigint";
}
interface IntrinsicBooleanType extends IntrinsicType {
intrinsicName: "boolean";
}
interface IntrinsicErrorType extends IntrinsicType {
intrinsicName: "error";
}
interface IntrinsicESSymbolType extends IntrinsicType {
intrinsicName: "symbol";
}
interface IntrinsicNeverType extends IntrinsicType {
intrinsicName: "never";
}
interface IntrinsicNonPrimitiveType extends IntrinsicType {
intrinsicName: "";
}
interface IntrinsicNullType extends IntrinsicType {
intrinsicName: "null";
}
interface IntrinsicNumberType extends IntrinsicType {
intrinsicName: "number";
}
interface IntrinsicStringType extends IntrinsicType {
intrinsicName: "string";
}
interface IntrinsicUndefinedType extends IntrinsicType {
intrinsicName: "undefined";
}
interface IntrinsicUnknownType extends IntrinsicType {
intrinsicName: "unknown";
}
interface IntrinsicVoidType extends IntrinsicType {
intrinsicName: "void";
}Test if a type is any intrinsic (built-in) type.
function isIntrinsicType(type: ts.Type): type is IntrinsicTypeTest if a type is the any intrinsic type.
function isIntrinsicAnyType(type: ts.Type): type is IntrinsicAnyTypeTest if a type is the bigint intrinsic type.
function isIntrinsicBigIntType(type: ts.Type): type is IntrinsicBigIntTypeTest if a type is the boolean intrinsic type.
function isIntrinsicBooleanType(type: ts.Type): type is IntrinsicBooleanTypeTest if a type is the error intrinsic type (occurs when TypeScript encounters resolution errors).
function isIntrinsicErrorType(type: ts.Type): type is IntrinsicErrorTypeTest if a type is the symbol intrinsic type.
function isIntrinsicESSymbolType(type: ts.Type): type is IntrinsicESSymbolTypeTest if a type is the never intrinsic type.
function isIntrinsicNeverType(type: ts.Type): type is IntrinsicNeverTypeTest if a type is a non-primitive intrinsic type (e.g., object).
function isIntrinsicNonPrimitiveType(type: ts.Type): type is IntrinsicNonPrimitiveTypeTest if a type is the null intrinsic type.
function isIntrinsicNullType(type: ts.Type): type is IntrinsicNullTypeTest if a type is the number intrinsic type.
function isIntrinsicNumberType(type: ts.Type): type is IntrinsicNumberTypeTest if a type is the string intrinsic type.
function isIntrinsicStringType(type: ts.Type): type is IntrinsicStringTypeTest if a type is the undefined intrinsic type.
function isIntrinsicUndefinedType(type: ts.Type): type is IntrinsicUndefinedTypeTest if a type is the unknown intrinsic type.
function isIntrinsicUnknownType(type: ts.Type): type is IntrinsicUnknownTypeTest if a type is the void intrinsic type.
function isIntrinsicVoidType(type: ts.Type): type is IntrinsicVoidTypeExample:
declare const type: ts.Type;
if (isIntrinsicStringType(type)) {
// Type is the built-in string type
} else if (isIntrinsicNumberType(type)) {
// Type is the built-in number type
} else if (isIntrinsicErrorType(type)) {
// TypeScript encountered an error resolving this type
}Functions that check for literal types (specific values like "hello" or 42).
interface BooleanLiteralType extends FreshableIntrinsicType {
intrinsicName: "false" | "true";
}
interface FalseLiteralType extends BooleanLiteralType {
intrinsicName: "false";
}
interface TrueLiteralType extends BooleanLiteralType {
intrinsicName: "true";
}
interface UnknownLiteralType extends FreshableIntrinsicType {
value?: unknown;
}Test if a type is a bigint literal type (e.g., 123n).
function isBigIntLiteralType(type: ts.Type): type is ts.BigIntLiteralTypeTest if a type is a boolean literal type (true or false).
function isBooleanLiteralType(type: ts.Type): type is BooleanLiteralTypeTest if a type is the false literal type.
function isFalseLiteralType(type: ts.Type): type is FalseLiteralTypeTest if a type is the true literal type.
function isTrueLiteralType(type: ts.Type): type is TrueLiteralTypeTest if a type is any kind of literal type.
function isLiteralType(type: ts.Type): type is ts.LiteralTypeTest if a type is a number literal type (e.g., 42).
function isNumberLiteralType(type: ts.Type): type is ts.NumberLiteralTypeTest if a type is a string literal type (e.g., "hello").
function isStringLiteralType(type: ts.Type): type is ts.StringLiteralTypeTest if a type is a template literal type (e.g., `hello ${string}`).
function isTemplateLiteralType(type: ts.Type): type is ts.TemplateLiteralTypeExample:
declare const type: ts.Type;
if (isStringLiteralType(type)) {
console.log(`String literal: ${type.value}`);
} else if (isNumberLiteralType(type)) {
console.log(`Number literal: ${type.value}`);
} else if (isTrueLiteralType(type)) {
console.log('Boolean literal: true');
}Functions that check for object-related types.
Test if a type is an evolving array type (used during type inference).
function isEvolvingArrayType(type: ts.Type): type is ts.EvolvingArrayTypeTest if a type is a tuple type.
function isTupleType(type: ts.Type): type is ts.TupleTypeTest if a type is a type reference (generic type instantiation).
function isTypeReference(type: ts.Type): type is ts.TypeReferenceExample:
declare const type: ts.Type;
if (isTupleType(type)) {
// Handle tuple type like [string, number]
} else if (isTypeReference(type)) {
// Handle generic type instantiation like Array<string>
}Functions that check for single, specific type categories.
Test if a type is a conditional type (T extends U ? X : Y).
function isConditionalType(type: ts.Type): type is ts.ConditionalTypeTest if a type is an enum type.
function isEnumType(type: ts.Type): type is ts.EnumTypeTest if a type is freshable (can have fresh literal types).
function isFreshableType(type: ts.Type): type is ts.FreshableTypeTest if a type is an indexed access type (T[K]).
function isIndexedAccessType(type: ts.Type): type is ts.IndexedAccessTypeTest if a type is an index type (keyof T).
function isIndexType(type: ts.Type): type is ts.IndexTypeTest if a type is instantiable (can be instantiated with type arguments).
function isInstantiableType(type: ts.Type): type is ts.InstantiableTypeTest if a type is an intersection type (A & B).
function isIntersectionType(type: ts.Type): type is ts.IntersectionTypeTest if a type is an object type.
function isObjectType(type: ts.Type): type is ts.ObjectTypeTest if a type is a string mapping type (like Uppercase<T>).
function isStringMappingType(type: ts.Type): type is ts.StringMappingTypeTest if a type is a substitution type.
function isSubstitutionType(type: ts.Type): type is ts.SubstitutionTypeTest if a type is a type parameter. Note: This is intentionally not a type guard.
function isTypeParameter(type: ts.Type): booleanTest if a type is a type variable.
function isTypeVariable(type: ts.Type): type is ts.TypeVariableTest if a type is either a union or intersection type.
function isUnionOrIntersectionType(type: ts.Type): type is ts.UnionOrIntersectionTypeTest if a type is a union type (A | B).
function isUnionType(type: ts.Type): type is ts.UnionTypeTest if a type is a unique ES symbol type.
function isUniqueESSymbolType(type: ts.Type): type is ts.UniqueESSymbolTypeFunctions that check for types with multiple characteristics.
interface FreshableIntrinsicType extends ts.FreshableType, IntrinsicType {}Test if a type is both intrinsic and freshable.
function isFreshableIntrinsicType(type: ts.Type): type is FreshableIntrinsicTypeTest if a type is a tuple type reference (reference to a tuple type).
function isTupleTypeReference(type: ts.Type): type is ts.TupleTypeReferenceHelper functions for working with and manipulating types.
Get the constituent types of an intersection type. If the type is not an intersection, returns an array containing only that type.
function intersectionTypeParts(type: ts.Type): ts.Type[]Parameters:
type: ts.Type - The type to decomposeReturns: Array of constituent types
Example:
declare const type: ts.Type;
for (const typePart of intersectionTypeParts(type)) {
// Process each part of the intersection (or the type itself)
}Get the constituent types of a union type. If the type is not a union, returns an array containing only that type.
function unionTypeParts(type: ts.Type): ts.Type[]Parameters:
type: ts.Type - The type to decomposeReturns: Array of constituent types
Example:
declare const type: ts.Type;
for (const typePart of unionTypeParts(type)) {
// Process each part of the union (or the type itself)
}Get the constituent types of either a union or intersection type. Returns type.types for union/intersection types, or [type] for other types.
function typeParts(type: ts.Type): ts.Type[]Parameters:
type: ts.Type - The type to decomposeReturns: Array of constituent types
Determine if a type is definitely falsy (but doesn't unwrap union types).
function isFalsyType(type: ts.Type): booleanParameters:
type: ts.Type - The type to checkReturns: true if the type is definitely falsy
Example:
declare const type: ts.Type;
if (isFalsyType(type)) {
// Type is falsy (null, undefined, false, 0, "", etc.)
}Determine whether writing to a specific property of a type is allowed.
function isPropertyReadonlyInType(
type: ts.Type,
name: ts.__String,
typeChecker: ts.TypeChecker
): booleanParameters:
type: ts.Type - The type containing the propertyname: ts.__String - The property name to checktypeChecker: ts.TypeChecker - Type checker for analysisReturns: true if the property is readonly
Example:
declare const property: ts.Symbol;
declare const type: ts.Type;
declare const typeChecker: ts.TypeChecker;
if (isPropertyReadonlyInType(type, property.getEscapedName(), typeChecker)) {
// Property cannot be written to
}Determine if a type is thenable (has a then method) and can be used with await.
function isThenableType(
typeChecker: ts.TypeChecker,
node: ts.Node,
type: ts.Type
): boolean
function isThenableType(
typeChecker: ts.TypeChecker,
node: ts.Expression,
type?: ts.Type
): booleanParameters:
typeChecker: ts.TypeChecker - Type checker for analysisnode: ts.Node | ts.Expression - The AST node for contexttype?: ts.Type - Optional type to check (inferred from node if not provided)Returns: true if the type is thenable
Example:
declare const node: ts.Node;
declare const type: ts.Type;
declare const typeChecker: ts.TypeChecker;
if (isThenableType(typeChecker, node, type)) {
// Type can be awaited
}Test if a symbol has any readonly declarations.
function symbolHasReadonlyDeclaration(
symbol: ts.Symbol,
typeChecker: ts.TypeChecker
): booleanParameters:
symbol: ts.Symbol - The symbol to checktypeChecker: ts.TypeChecker - Type checker for analysisReturns: true if the symbol has readonly declarations
Example:
declare const symbol: ts.Symbol;
declare const typeChecker: ts.TypeChecker;
if (symbolHasReadonlyDeclaration(symbol, typeChecker)) {
// Symbol is declared as readonly
}Test if a type is a literal type, with proper handling for bigint literals in older TypeScript versions.
function typeIsLiteral(type: ts.Type): type is ts.LiteralTypeParameters:
type: ts.Type - The type to checkReturns: true if the type is a literal type
Note: This function provides a compatibility layer for TypeScript versions before 5.0, where type.isLiteral() didn't correctly handle bigint literals.
Example:
declare const type: ts.Type;
if (typeIsLiteral(type)) {
// Type is a literal (string, number, bigint, etc.)
}function analyzeCallableType(type: ts.Type, typeChecker: ts.TypeChecker) {
const signatures = getCallSignaturesOfType(type);
if (signatures.length === 0) {
console.log('Type is not callable');
return;
}
console.log(`Type has ${signatures.length} call signature(s)`);
for (const signature of signatures) {
const params = signature.getParameters();
console.log(` ${params.length} parameter(s)`);
}
}function classifyType(type: ts.Type): string {
if (isIntrinsicType(type)) {
return `Intrinsic: ${type.intrinsicName || 'unknown'}`;
}
if (isLiteralType(type)) {
if (isStringLiteralType(type)) {
return `String literal: "${type.value}"`;
} else if (isNumberLiteralType(type)) {
return `Number literal: ${type.value}`;
} else if (isBooleanLiteralType(type)) {
return `Boolean literal: ${type.intrinsicName}`;
}
return 'Literal type';
}
if (isUnionType(type)) {
const parts = unionTypeParts(type);
return `Union of ${parts.length} types`;
}
if (isIntersectionType(type)) {
const parts = intersectionTypeParts(type);
return `Intersection of ${parts.length} types`;
}
return 'Other type';
}function analyzeProperty(
type: ts.Type,
propertyName: string,
typeChecker: ts.TypeChecker
) {
const escapedName = propertyName as ts.__String;
const property = getPropertyOfType(type, escapedName);
if (!property) {
console.log(`Property '${propertyName}' not found`);
return;
}
const isReadonly = isPropertyReadonlyInType(type, escapedName, typeChecker);
console.log(`Property '${propertyName}' is ${isReadonly ? 'readonly' : 'mutable'}`);
if (symbolHasReadonlyDeclaration(property, typeChecker)) {
console.log('Property has readonly declaration');
}
}The Type System Utilities provide the foundational tools needed for sophisticated TypeScript analysis, enabling developers to build tools that understand and work with TypeScript's rich type system in a reliable and comprehensive way.
Install with Tessl CLI
npx tessl i tessl/npm-ts-api-utilsevals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10