CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-typescript-eslint--type-utils

Type utilities for working with TypeScript + ESLint together

Pending
Overview
Eval results
Files

builtin-types.mddocs/

Builtin Type Checks

Specialized predicates for identifying built-in TypeScript types like Promise, Error, and readonly utility types. These functions help recognize common patterns and built-in type constructs in TypeScript code.

Capabilities

Promise Type Checks

Functions for identifying Promise-related types and patterns.

/**
 * Checks if a type is Promise-like (extends Promise)
 * Example: class DerivedClass extends Promise<number> {} - DerivedClass is Promise-like
 */
function isPromiseLike(program: ts.Program, type: ts.Type): boolean;

/**
 * Checks if a type is PromiseConstructor-like
 * Example: const value = Promise; value.reject - value is PromiseConstructor-like
 */
function isPromiseConstructorLike(program: ts.Program, type: ts.Type): boolean;

Usage Examples:

import { isPromiseLike, isPromiseConstructorLike } from "@typescript-eslint/type-utils";

// In an ESLint rule checking async patterns
export default {
  create(context) {
    const services = context.parserServices;
    const program = services.program;
    const checker = program.getTypeChecker();

    return {
      VariableDeclarator(node) {
        if (node.init) {
          const tsNode = services.esTreeNodeToTSNodeMap.get(node.init);
          const type = checker.getTypeAtLocation(tsNode);
          
          if (isPromiseLike(program, type)) {
            // Handle Promise-like types
            context.report({
              node,
              messageId: "promiseDetected",
              data: { typeName: checker.typeToString(type) }
            });
          }
          
          if (isPromiseConstructorLike(program, type)) {
            // Handle Promise constructor references
            context.report({
              node,
              messageId: "promiseConstructorUsage"
            });
          }
        }
      }
    };
  }
};

Error Type Checks

Functions for identifying Error-related types and inheritance patterns.

/**
 * Checks if a type extends the Error class
 * Example: class Foo extends Error {} - new Foo() is Error-like
 */
function isErrorLike(program: ts.Program, type: ts.Type): boolean;

/**
 * Checks if a type is a readonly Error type (like Readonly<Error>)
 */
function isReadonlyErrorLike(program: ts.Program, type: ts.Type): boolean;

Usage Examples:

import { isErrorLike, isReadonlyErrorLike } from "@typescript-eslint/type-utils";

// In an ESLint rule for error handling
export default {
  create(context) {
    const services = context.parserServices;
    const program = services.program;
    const checker = program.getTypeChecker();

    return {
      ThrowStatement(node) {
        const tsNode = services.esTreeNodeToTSNodeMap.get(node.argument);
        const type = checker.getTypeAtLocation(tsNode);
        
        if (!isErrorLike(program, type)) {
          context.report({
            node: node.argument,
            messageId: "throwNonError"
          });
        }
      },
      
      CatchClause(node) {
        if (node.param) {
          const tsNode = services.esTreeNodeToTSNodeMap.get(node.param);
          const type = checker.getTypeAtLocation(tsNode);
          
          if (isReadonlyErrorLike(program, type)) {
            context.report({
              node: node.param,
              messageId: "readonlyErrorInCatch"
            });
          }
        }
      }
    };
  }
};

Readonly Utility Type Checks

Functions for identifying TypeScript's built-in readonly utility types.

/**
 * Checks if a type is a Readonly type alias
 * Example: type T = Readonly<{ foo: 'bar' }> - T is ReadonlyTypeLike
 */
function isReadonlyTypeLike(
  program: ts.Program, 
  type: ts.Type, 
  predicate?: (subType: { aliasSymbol: ts.Symbol; aliasTypeArguments: readonly ts.Type[] } & ts.Type) => boolean
): boolean;

Usage Examples:

import { isReadonlyTypeLike } from "@typescript-eslint/type-utils";

// Check for Readonly utility type usage
export default {
  create(context) {
    const services = context.parserServices;
    const program = services.program;
    const checker = program.getTypeChecker();

    return {
      TSTypeReference(node) {
        const tsNode = services.esTreeNodeToTSNodeMap.get(node);
        const type = checker.getTypeAtLocation(tsNode);
        
        if (isReadonlyTypeLike(program, type)) {
          // Found Readonly<T> usage
          context.report({
            node,
            messageId: "readonlyUtilityType"
          });
        }
      }
    };
  }
};

Generic Builtin Type Checking

Generic functions for building custom builtin type checkers.

/**
 * Generic function to check if a type matches a built-in type alias with a predicate
 */
function isBuiltinTypeAliasLike(
  program: ts.Program, 
  type: ts.Type, 
  predicate: (subType: {aliasSymbol: ts.Symbol; aliasTypeArguments: readonly ts.Type[]} & ts.Type) => boolean
): boolean;

/**
 * Checks if a type is like a built-in symbol with the given name(s)
 */
function isBuiltinSymbolLike(
  program: ts.Program, 
  type: ts.Type, 
  symbolName: string | string[]
): boolean;

/**
 * Recursive helper function for built-in symbol-like checks. 
 * Handles inheritance, unions, intersections, and type parameters.
 */
function isBuiltinSymbolLikeRecurser(
  program: ts.Program, 
  type: ts.Type, 
  predicate: (subType: ts.Type) => boolean | null
): boolean;

Usage Examples:

import { 
  isBuiltinTypeAliasLike, 
  isBuiltinSymbolLike, 
  isBuiltinSymbolLikeRecurser 
} from "@typescript-eslint/type-utils";

// Custom builtin type checker for Record<K, V>
function isRecordLike(program: ts.Program, type: ts.Type): boolean {
  return isBuiltinTypeAliasLike(program, type, (subType) => {
    return subType.aliasSymbol?.name === 'Record' && 
           subType.aliasTypeArguments?.length === 2;
  });
}

// Check for Map or Set types
function isMapOrSetLike(program: ts.Program, type: ts.Type): boolean {
  return isBuiltinSymbolLike(program, type, ['Map', 'Set']);
}

// Custom predicate-based checker
function isIterableLike(program: ts.Program, type: ts.Type): boolean {
  return isBuiltinSymbolLikeRecurser(program, type, (subType) => {
    // Check if type has Symbol.iterator method
    const checker = program.getTypeChecker();
    const iteratorSymbol = checker.getPropertyOfType(subType, '__@iterator');
    return iteratorSymbol ? true : null;
  });
}

// Usage in ESLint rule
export default {
  create(context) {
    const services = context.parserServices;
    const program = services.program;
    const checker = program.getTypeChecker();

    return {
      VariableDeclarator(node) {
        if (node.init) {
          const tsNode = services.esTreeNodeToTSNodeMap.get(node.init);
          const type = checker.getTypeAtLocation(tsNode);
          
          if (isRecordLike(program, type)) {
            // Handle Record types
          }
          
          if (isMapOrSetLike(program, type)) {
            // Handle Map/Set types
          }
          
          if (isIterableLike(program, type)) {
            // Handle iterable types
          }
        }
      }
    };
  }
};

Advanced Builtin Type Patterns

Custom Type Family Checkers

// Example: Creating a checker for all array-like builtin types
import { isBuiltinSymbolLikeRecurser } from "@typescript-eslint/type-utils";

function isArrayLikeBuiltin(program: ts.Program, type: ts.Type): boolean {
  return isBuiltinSymbolLikeRecurser(program, type, (subType) => {
    const checker = program.getTypeChecker();
    const symbol = subType.symbol || subType.aliasSymbol;
    
    if (!symbol) return null;
    
    const arrayLikeNames = [
      'Array', 'ReadonlyArray', 'Int8Array', 'Uint8Array', 
      'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
      'Float32Array', 'Float64Array', 'ArrayBuffer'
    ];
    
    return arrayLikeNames.includes(symbol.name) ? true : null;
  });
}

Promise Pattern Detection

// Example: Comprehensive Promise pattern analysis
import { isPromiseLike, isPromiseConstructorLike } from "@typescript-eslint/type-utils";

function analyzePromisePatterns(
  program: ts.Program,
  type: ts.Type,
  checker: ts.TypeChecker
): {
  isPromise: boolean;
  isConstructor: boolean;
  isThenable: boolean;
} {
  const isPromise = isPromiseLike(program, type);
  const isConstructor = isPromiseConstructorLike(program, type);
  
  // Check for thenable (has .then method)
  const thenProperty = checker.getPropertyOfType(type, 'then');
  const isThenable = !!thenProperty;
  
  return { isPromise, isConstructor, isThenable };
}

Error Hierarchy Analysis

// Example: Analyzing error type hierarchies
import { isErrorLike, isReadonlyErrorLike } from "@typescript-eslint/type-utils";

function analyzeErrorHierarchy(
  program: ts.Program,
  type: ts.Type,
  checker: ts.TypeChecker
): {
  isError: boolean;
  isReadonlyError: boolean;
  errorName: string | null;
  customError: boolean;
} {
  const isError = isErrorLike(program, type);
  const isReadonlyError = isReadonlyErrorLike(program, type);
  
  let errorName: string | null = null;
  let customError = false;
  
  if (isError) {
    const symbol = type.symbol || type.aliasSymbol;
    errorName = symbol?.name || null;
    
    // Check if it's a custom error (not built-in Error)
    customError = errorName !== 'Error' && errorName !== null;
  }
  
  return { isError, isReadonlyError, errorName, customError };
}

Install with Tessl CLI

npx tessl i tessl/npm-typescript-eslint--type-utils

docs

builtin-types.md

index.md

property-types.md

symbol-analysis.md

type-analysis.md

type-constraints.md

type-predicates.md

type-safety.md

type-specifiers.md

tile.json