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

type-constraints.mddocs/

Type Constraints and Context

Tools for working with TypeScript's type constraint system and contextual typing. These utilities are essential for understanding generic types, type inference, and the relationships between types in different contexts.

Capabilities

Type Constraint Resolution

Functions for resolving generic type constraints and understanding type parameter relationships.

/**
 * Resolves a node's type, returning the type's generic constraint if it has one.
 * Warning: If the type is generic without a constraint, returns the type as-is 
 * rather than `unknown`. Check for ts.TypeFlags.TypeParameter to detect this case.
 */
function getConstrainedTypeAtLocation(
  services: ParserServicesWithTypeInformation, 
  node: TSESTree.Node
): ts.Type;

Usage Examples:

import { getConstrainedTypeAtLocation } from "@typescript-eslint/type-utils";
import * as ts from "typescript";

// In an ESLint rule
export default {
  create(context) {
    const services = context.parserServices;

    return {
      FunctionDeclaration(node) {
        // Get the constrained type for a function parameter
        if (node.params.length > 0) {
          const param = node.params[0];
          const constrainedType = getConstrainedTypeAtLocation(services, param);
          
          // Check if it's a type parameter without constraint
          if (constrainedType.flags & ts.TypeFlags.TypeParameter) {
            // Handle unconstrained type parameter
            context.report({
              node: param,
              messageId: "unconstrainedGeneric"
            });
          }
        }
      }
    };
  }
};

Contextual Type Analysis

Functions for understanding the contextual type expectations in TypeScript expressions.

/**
 * Returns the contextual type of a given node - the type of the target 
 * the node is going into (e.g., function parameter type, variable declaration type)
 */
function getContextualType(checker: ts.TypeChecker, node: ts.Expression): ts.Type | undefined;

Usage Examples:

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

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

    return {
      CallExpression(node) {
        // Check each argument against its expected contextual type
        node.arguments.forEach((arg, index) => {
          const tsArg = services.esTreeNodeToTSNodeMap.get(arg);
          const contextualType = getContextualType(checker, tsArg);
          
          if (contextualType) {
            const actualType = checker.getTypeAtLocation(tsArg);
            
            // Compare actual vs expected type
            if (!checker.isTypeAssignableTo(actualType, contextualType)) {
              context.report({
                node: arg,
                messageId: "typeArgumentMismatch",
                data: {
                  expected: checker.typeToString(contextualType),
                  actual: checker.typeToString(actualType)
                }
              });
            }
          }
        });
      }
    };
  }
};

Advanced Constraint Analysis

Generic Type Constraint Checking

// Example: Analyzing generic constraints in function declarations
import { getConstrainedTypeAtLocation } from "@typescript-eslint/type-utils";
import * as ts from "typescript";

function analyzeGenericConstraints(
  services: ParserServicesWithTypeInformation,
  functionNode: TSESTree.FunctionDeclaration
) {
  if (functionNode.typeParameters) {
    functionNode.typeParameters.params.forEach(typeParam => {
      const constrainedType = getConstrainedTypeAtLocation(services, typeParam);
      
      if (constrainedType.flags & ts.TypeFlags.TypeParameter) {
        // Unconstrained type parameter
        console.log(`Unconstrained type parameter: ${typeParam.name.name}`);
      } else {
        // Has constraint
        const checker = services.program.getTypeChecker();
        const constraintName = checker.typeToString(constrainedType);
        console.log(`Type parameter ${typeParam.name.name} constrained to: ${constraintName}`);
      }
    });
  }
}

Contextual Type Validation

// Example: Validating array literal elements against contextual type
import { getContextualType } from "@typescript-eslint/type-utils";

function validateArrayLiteralTypes(
  services: ParserServicesWithTypeInformation,
  arrayNode: TSESTree.ArrayExpression
) {
  const checker = services.program.getTypeChecker();
  const tsArrayNode = services.esTreeNodeToTSNodeMap.get(arrayNode);
  const contextualType = getContextualType(checker, tsArrayNode);
  
  if (contextualType && checker.isArrayType(contextualType)) {
    const elementType = checker.getTypeArguments(contextualType as ts.TypeReference)[0];
    
    arrayNode.elements.forEach(element => {
      if (element) {
        const tsElement = services.esTreeNodeToTSNodeMap.get(element);
        const elementActualType = checker.getTypeAtLocation(tsElement);
        
        if (!checker.isTypeAssignableTo(elementActualType, elementType)) {
          console.log("Array element type mismatch");
        }
      }
    });
  }
}

Inference Context Analysis

// Example: Understanding type inference in variable declarations
import { getContextualType, getConstrainedTypeAtLocation } from "@typescript-eslint/type-utils";

function analyzeTypeInference(
  services: ParserServicesWithTypeInformation,
  variableDeclarator: TSESTree.VariableDeclarator
) {
  const checker = services.program.getTypeChecker();
  
  // Get the declared type constraint
  if (variableDeclarator.id.typeAnnotation) {
    const declaredType = getConstrainedTypeAtLocation(services, variableDeclarator.id);
    console.log(`Declared type: ${checker.typeToString(declaredType)}`);
  }
  
  // Get the contextual type from initializer
  if (variableDeclarator.init) {
    const tsInit = services.esTreeNodeToTSNodeMap.get(variableDeclarator.init);
    const contextualType = getContextualType(checker, tsInit);
    
    if (contextualType) {
      console.log(`Contextual type: ${checker.typeToString(contextualType)}`);
    }
    
    // Compare inferred vs contextual
    const inferredType = checker.getTypeAtLocation(tsInit);
    console.log(`Inferred type: ${checker.typeToString(inferredType)}`);
  }
}

Common Patterns

Generic Function Analysis

// Analyze generic function constraints and usage
function analyzeGenericFunction(node: TSESTree.FunctionDeclaration, services: ParserServicesWithTypeInformation) {
  const checker = services.program.getTypeChecker();
  
  // Check type parameters
  if (node.typeParameters) {
    node.typeParameters.params.forEach(param => {
      const constrainedType = getConstrainedTypeAtLocation(services, param);
      // Analyze constraint
    });
  }
  
  // Check parameter contextual types
  node.params.forEach(param => {
    if (param.type === "Identifier" && param.init) {
      const tsInit = services.esTreeNodeToTSNodeMap.get(param.init);
      const contextualType = getContextualType(checker, tsInit);
      // Validate default parameter against contextual type
    }
  });
}

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