CtrlK
BlogDocsLog inGet started
Tessl Logo

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.

79

1.97x
Overview
Eval results
Files

type-system.mddocs/

Type System Utilities

Overview

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.

Core Concepts

Types

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.

Type Checking

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

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.

Type Getters

Functions that extract or derive type information from TypeScript types.

getCallSignaturesOfType

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 from

Returns: 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
}

getPropertyOfType

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 | undefined

Parameters:

  • type: ts.Type - The type to search for the property
  • name: ts.__String - The escaped property name to look for

Returns: 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
}

getWellKnownSymbolPropertyOfType

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 | undefined

Parameters:

  • type: ts.Type - The type to search in
  • wellKnownSymbolName: string - The well-known symbol name (e.g., "asyncIterator", "iterator")
  • typeChecker: ts.TypeChecker - The type checker for resolving symbols

Returns: 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
);

Type Guards

Intrinsic Type Guards

Functions that check for TypeScript's built-in intrinsic types.

Type Definitions

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";
}

isIntrinsicType

Test if a type is any intrinsic (built-in) type.

function isIntrinsicType(type: ts.Type): type is IntrinsicType

isIntrinsicAnyType

Test if a type is the any intrinsic type.

function isIntrinsicAnyType(type: ts.Type): type is IntrinsicAnyType

isIntrinsicBigIntType

Test if a type is the bigint intrinsic type.

function isIntrinsicBigIntType(type: ts.Type): type is IntrinsicBigIntType

isIntrinsicBooleanType

Test if a type is the boolean intrinsic type.

function isIntrinsicBooleanType(type: ts.Type): type is IntrinsicBooleanType

isIntrinsicErrorType

Test if a type is the error intrinsic type (occurs when TypeScript encounters resolution errors).

function isIntrinsicErrorType(type: ts.Type): type is IntrinsicErrorType

isIntrinsicESSymbolType

Test if a type is the symbol intrinsic type.

function isIntrinsicESSymbolType(type: ts.Type): type is IntrinsicESSymbolType

isIntrinsicNeverType

Test if a type is the never intrinsic type.

function isIntrinsicNeverType(type: ts.Type): type is IntrinsicNeverType

isIntrinsicNonPrimitiveType

Test if a type is a non-primitive intrinsic type (e.g., object).

function isIntrinsicNonPrimitiveType(type: ts.Type): type is IntrinsicNonPrimitiveType

isIntrinsicNullType

Test if a type is the null intrinsic type.

function isIntrinsicNullType(type: ts.Type): type is IntrinsicNullType

isIntrinsicNumberType

Test if a type is the number intrinsic type.

function isIntrinsicNumberType(type: ts.Type): type is IntrinsicNumberType

isIntrinsicStringType

Test if a type is the string intrinsic type.

function isIntrinsicStringType(type: ts.Type): type is IntrinsicStringType

isIntrinsicUndefinedType

Test if a type is the undefined intrinsic type.

function isIntrinsicUndefinedType(type: ts.Type): type is IntrinsicUndefinedType

isIntrinsicUnknownType

Test if a type is the unknown intrinsic type.

function isIntrinsicUnknownType(type: ts.Type): type is IntrinsicUnknownType

isIntrinsicVoidType

Test if a type is the void intrinsic type.

function isIntrinsicVoidType(type: ts.Type): type is IntrinsicVoidType

Example:

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
}

Literal Type Guards

Functions that check for literal types (specific values like "hello" or 42).

Type Definitions

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;
}

isBigIntLiteralType

Test if a type is a bigint literal type (e.g., 123n).

function isBigIntLiteralType(type: ts.Type): type is ts.BigIntLiteralType

isBooleanLiteralType

Test if a type is a boolean literal type (true or false).

function isBooleanLiteralType(type: ts.Type): type is BooleanLiteralType

isFalseLiteralType

Test if a type is the false literal type.

function isFalseLiteralType(type: ts.Type): type is FalseLiteralType

isTrueLiteralType

Test if a type is the true literal type.

function isTrueLiteralType(type: ts.Type): type is TrueLiteralType

isLiteralType

Test if a type is any kind of literal type.

function isLiteralType(type: ts.Type): type is ts.LiteralType

isNumberLiteralType

Test if a type is a number literal type (e.g., 42).

function isNumberLiteralType(type: ts.Type): type is ts.NumberLiteralType

isStringLiteralType

Test if a type is a string literal type (e.g., "hello").

function isStringLiteralType(type: ts.Type): type is ts.StringLiteralType

isTemplateLiteralType

Test if a type is a template literal type (e.g., `hello ${string}`).

function isTemplateLiteralType(type: ts.Type): type is ts.TemplateLiteralType

Example:

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');
}

Object Type Guards

Functions that check for object-related types.

isEvolvingArrayType

Test if a type is an evolving array type (used during type inference).

function isEvolvingArrayType(type: ts.Type): type is ts.EvolvingArrayType

isTupleType

Test if a type is a tuple type.

function isTupleType(type: ts.Type): type is ts.TupleType

isTypeReference

Test if a type is a type reference (generic type instantiation).

function isTypeReference(type: ts.Type): type is ts.TypeReference

Example:

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>
}

Single Type Guards

Functions that check for single, specific type categories.

isConditionalType

Test if a type is a conditional type (T extends U ? X : Y).

function isConditionalType(type: ts.Type): type is ts.ConditionalType

isEnumType

Test if a type is an enum type.

function isEnumType(type: ts.Type): type is ts.EnumType

isFreshableType

Test if a type is freshable (can have fresh literal types).

function isFreshableType(type: ts.Type): type is ts.FreshableType

isIndexedAccessType

Test if a type is an indexed access type (T[K]).

function isIndexedAccessType(type: ts.Type): type is ts.IndexedAccessType

isIndexType

Test if a type is an index type (keyof T).

function isIndexType(type: ts.Type): type is ts.IndexType

isInstantiableType

Test if a type is instantiable (can be instantiated with type arguments).

function isInstantiableType(type: ts.Type): type is ts.InstantiableType

isIntersectionType

Test if a type is an intersection type (A & B).

function isIntersectionType(type: ts.Type): type is ts.IntersectionType

isObjectType

Test if a type is an object type.

function isObjectType(type: ts.Type): type is ts.ObjectType

isStringMappingType

Test if a type is a string mapping type (like Uppercase<T>).

function isStringMappingType(type: ts.Type): type is ts.StringMappingType

isSubstitutionType

Test if a type is a substitution type.

function isSubstitutionType(type: ts.Type): type is ts.SubstitutionType

isTypeParameter

Test if a type is a type parameter. Note: This is intentionally not a type guard.

function isTypeParameter(type: ts.Type): boolean

isTypeVariable

Test if a type is a type variable.

function isTypeVariable(type: ts.Type): type is ts.TypeVariable

isUnionOrIntersectionType

Test if a type is either a union or intersection type.

function isUnionOrIntersectionType(type: ts.Type): type is ts.UnionOrIntersectionType

isUnionType

Test if a type is a union type (A | B).

function isUnionType(type: ts.Type): type is ts.UnionType

isUniqueESSymbolType

Test if a type is a unique ES symbol type.

function isUniqueESSymbolType(type: ts.Type): type is ts.UniqueESSymbolType

Compound Type Guards

Functions that check for types with multiple characteristics.

Type Definitions

interface FreshableIntrinsicType extends ts.FreshableType, IntrinsicType {}

isFreshableIntrinsicType

Test if a type is both intrinsic and freshable.

function isFreshableIntrinsicType(type: ts.Type): type is FreshableIntrinsicType

isTupleTypeReference

Test if a type is a tuple type reference (reference to a tuple type).

function isTupleTypeReference(type: ts.Type): type is ts.TupleTypeReference

Type Utilities

Helper functions for working with and manipulating types.

intersectionTypeParts

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 decompose

Returns: 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)
}

unionTypeParts

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 decompose

Returns: 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)
}

typeParts

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 decompose

Returns: Array of constituent types

isFalsyType

Determine if a type is definitely falsy (but doesn't unwrap union types).

function isFalsyType(type: ts.Type): boolean

Parameters:

  • type: ts.Type - The type to check

Returns: true if the type is definitely falsy

Example:

declare const type: ts.Type;

if (isFalsyType(type)) {
  // Type is falsy (null, undefined, false, 0, "", etc.)
}

isPropertyReadonlyInType

Determine whether writing to a specific property of a type is allowed.

function isPropertyReadonlyInType(
  type: ts.Type, 
  name: ts.__String, 
  typeChecker: ts.TypeChecker
): boolean

Parameters:

  • type: ts.Type - The type containing the property
  • name: ts.__String - The property name to check
  • typeChecker: ts.TypeChecker - Type checker for analysis

Returns: 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
}

isThenableType

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
): boolean

Parameters:

  • typeChecker: ts.TypeChecker - Type checker for analysis
  • node: ts.Node | ts.Expression - The AST node for context
  • type?: 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
}

symbolHasReadonlyDeclaration

Test if a symbol has any readonly declarations.

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

Parameters:

  • symbol: ts.Symbol - The symbol to check
  • typeChecker: ts.TypeChecker - Type checker for analysis

Returns: 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
}

typeIsLiteral

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.LiteralType

Parameters:

  • type: ts.Type - The type to check

Returns: 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.)
}

Usage Examples

Analyzing Function Types

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)`);
  }
}

Type Classification

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';
}

Property Analysis

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-utils

docs

compiler-options.md

index.md

node-analysis.md

syntax-utilities.md

type-system.md

usage-analysis.md

tile.json