sicua
Version:
A tool for analyzing project structure and dependencies
224 lines (223 loc) • 8.13 kB
JavaScript
"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;