UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

224 lines (223 loc) 8.13 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeResolver = void 0; const typescript_1 = __importDefault(require("typescript")); /** * Utility class for resolving TypeScript types accurately */ class TypeResolver { constructor(typeChecker) { this.typeChecker = typeChecker; } /** * Resolves the return type of a function-like declaration * Handles both explicit and inferred types */ resolveFunctionReturnType(node) { try { // First try to get explicit return type annotation if (node.type) { return this.cleanTypeString(node.type.getText()); } // For inferred types, use the type checker const signature = this.typeChecker.getSignatureFromDeclaration(node); if (signature) { const returnType = this.typeChecker.getReturnTypeOfSignature(signature); const typeString = this.typeChecker.typeToString(returnType); return this.cleanTypeString(typeString); } // Handle arrow functions without explicit types if (typescript_1.default.isArrowFunction(node) && node.body) { if (typescript_1.default.isBlock(node.body)) { // Block body - try to infer from return statements return this.inferReturnTypeFromBlock(node.body); } else { // Expression body - get the type of the expression const expressionType = this.typeChecker.getTypeAtLocation(node.body); return this.cleanTypeString(this.typeChecker.typeToString(expressionType)); } } return "unknown"; } catch (error) { return "unknown"; } } /** * Resolves parameter types for a function * Returns an array of type strings corresponding to each parameter */ resolveParameterTypes(node) { try { return node.parameters.map((param) => { // Check for explicit type annotation if (param.type) { return this.cleanTypeString(param.type.getText()); } // Use type checker for inferred types const paramType = this.typeChecker.getTypeAtLocation(param); return this.cleanTypeString(this.typeChecker.typeToString(paramType)); }); } catch (error) { return node.parameters.map(() => "unknown"); } } /** * Checks if a function returns a Promise (is effectively async) */ isEffectivelyAsync(node) { try { // Check for explicit async modifier if (node.modifiers?.some((mod) => mod.kind === typescript_1.default.SyntaxKind.AsyncKeyword)) { return true; } // Check return type for Promise const signature = this.typeChecker.getSignatureFromDeclaration(node); if (signature) { const returnType = this.typeChecker.getReturnTypeOfSignature(signature); const typeString = this.typeChecker.typeToString(returnType); return this.isPromiseType(typeString); } // For arrow functions, check the body if (typescript_1.default.isArrowFunction(node) && node.body && !typescript_1.default.isBlock(node.body)) { const expressionType = this.typeChecker.getTypeAtLocation(node.body); const typeString = this.typeChecker.typeToString(expressionType); return this.isPromiseType(typeString); } return false; } catch (error) { return false; } } /** * Resolves the type of a variable or property */ resolveVariableType(node) { try { // Check for explicit type annotation if (node.type) { return this.cleanTypeString(node.type.getText()); } // Use type checker for inferred types const nodeType = this.typeChecker.getTypeAtLocation(node); return this.cleanTypeString(this.typeChecker.typeToString(nodeType)); } catch (error) { return "unknown"; } } /** * Checks if a type represents a generic type */ isGenericType(typeString) { return typeString.includes("<") && typeString.includes(">"); } /** * Extracts generic type parameters from a type string */ extractGenericParameters(typeString) { const match = typeString.match(/<([^>]+)>/); if (match) { return match[1].split(",").map((param) => param.trim()); } return []; } /** * Checks if a type is a union type */ isUnionType(typeString) { return typeString.includes(" | "); } /** * Checks if a type is an intersection type */ isIntersectionType(typeString) { return typeString.includes(" & "); } /** * Private helper to clean up type strings */ cleanTypeString(typeString) { return typeString.replace(/\s+/g, " ").replace(/\n/g, " ").trim(); } /** * Private helper to check if a type string represents a Promise */ isPromiseType(typeString) { const cleanType = typeString.toLowerCase(); return (cleanType.includes("promise<") || cleanType.startsWith("promise") || cleanType.includes("awaitable") || cleanType.includes("thenable")); } /** * Private helper to infer return type from a block statement */ inferReturnTypeFromBlock(block) { const returnStatements = []; const findReturns = (node) => { if (typescript_1.default.isReturnStatement(node)) { returnStatements.push(node); } typescript_1.default.forEachChild(node, findReturns); }; findReturns(block); if (returnStatements.length === 0) { return "void"; } // If all returns have expressions, infer from the first one const firstReturnWithExpression = returnStatements.find((stmt) => stmt.expression); if (firstReturnWithExpression && firstReturnWithExpression.expression) { try { const expressionType = this.typeChecker.getTypeAtLocation(firstReturnWithExpression.expression); return this.cleanTypeString(this.typeChecker.typeToString(expressionType)); } catch (error) { return "unknown"; } } // If we only have empty returns, it's void return "void"; } /** * Checks if a type is a primitive type */ isPrimitiveType(typeString) { const primitives = [ "string", "number", "boolean", "null", "undefined", "void", "never", ]; return primitives.includes(typeString.toLowerCase()); } /** * Simplifies complex type strings for better readability */ simplifyTypeString(typeString) { const cleaned = this.cleanTypeString(typeString); // Handle very long union types if (this.isUnionType(cleaned) && cleaned.length > 100) { const unionTypes = cleaned.split(" | "); if (unionTypes.length > 5) { return `${unionTypes.slice(0, 3).join(" | ")} | ... (${unionTypes.length - 3} more)`; } } // Simplify deeply nested generics if (this.isGenericType(cleaned) && cleaned.length > 150) { const baseType = cleaned.split("<")[0]; return `${baseType}<...>`; } return cleaned; } } exports.TypeResolver = TypeResolver;