This commit is contained in:
142
src/utils/typeUtils.ts
Normal file
142
src/utils/typeUtils.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import type * as TSESTree from '@typescript-eslint/typescript-estree';
|
||||
|
||||
export function getTypeName(node: TSESTree.Node): string | null {
|
||||
switch (node.type) {
|
||||
case 'TSTypeReference':
|
||||
if (node.typeName.type === 'Identifier') {
|
||||
return node.typeName.name;
|
||||
}
|
||||
break;
|
||||
case 'TSQualifiedName':
|
||||
if (node.left.type === 'Identifier' && node.right.type === 'Identifier') {
|
||||
return `${node.left.name}.${node.right.name}`;
|
||||
}
|
||||
break;
|
||||
case 'Identifier':
|
||||
return node.name;
|
||||
case 'TSInterfaceDeclaration':
|
||||
return node.id.name;
|
||||
case 'TSTypeAliasDeclaration':
|
||||
return node.id.name;
|
||||
case 'TSEnumDeclaration':
|
||||
return node.id.name;
|
||||
case 'ClassDeclaration':
|
||||
return node.id?.name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function extractTypeAnnotation(node: TSESTree.Node): string {
|
||||
if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
|
||||
return typeToString(node.typeAnnotation.typeAnnotation);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
export function typeToString(node: TSESTree.Node): string {
|
||||
switch (node.type) {
|
||||
case 'TSStringKeyword':
|
||||
return 'string';
|
||||
case 'TSNumberKeyword':
|
||||
return 'number';
|
||||
case 'TSBooleanKeyword':
|
||||
return 'boolean';
|
||||
case 'TSNullKeyword':
|
||||
return 'null';
|
||||
case 'TSUndefinedKeyword':
|
||||
return 'undefined';
|
||||
case 'TSVoidKeyword':
|
||||
return 'void';
|
||||
case 'TSNeverKeyword':
|
||||
return 'never';
|
||||
case 'TSAnyKeyword':
|
||||
return 'any';
|
||||
case 'TSUnknownKeyword':
|
||||
return 'unknown';
|
||||
case 'TSBigIntKeyword':
|
||||
return 'bigint';
|
||||
case 'TSSymbolKeyword':
|
||||
return 'symbol';
|
||||
case 'TSArrayType':
|
||||
return `${typeToString(node.elementType)}[]`;
|
||||
case 'TSTupleType':
|
||||
return `[${node.elementTypes.map((el: TSESTree.TSTupleElement) => typeToString(el)).join(', ')}]`;
|
||||
case 'TSUnionType':
|
||||
return node.types.map((t: TSESTree.Node) => typeToString(t)).join(' | ');
|
||||
case 'TSIntersectionType':
|
||||
return node.types.map((t: TSESTree.Node) => typeToString(t)).join(' & ');
|
||||
case 'TSTypeReference':
|
||||
if (node.typeName.type === 'Identifier') {
|
||||
const typeArgs = node.typeParameters?.params
|
||||
? `<${node.typeParameters.params.map((p: TSESTree.Node) => typeToString(p)).join(', ')}>`
|
||||
: '';
|
||||
return `${node.typeName.name}${typeArgs}`;
|
||||
}
|
||||
break;
|
||||
case 'TSTypeLiteral':
|
||||
return `{ ${node.members.map((m: TSESTree.Node) => typeToString(m)).join('; ')} }`;
|
||||
case 'TSPropertySignature': {
|
||||
const key = node.key.type === 'Identifier' ? node.key.name : typeToString(node.key);
|
||||
const optional = node.optional ? '?' : '';
|
||||
return `${key}${optional}: ${typeToString(node.typeAnnotation?.typeAnnotation || node)}`;
|
||||
}
|
||||
case 'TSCallSignatureDeclaration':
|
||||
return '()';
|
||||
case 'TSConstructSignatureDeclaration':
|
||||
return 'new()';
|
||||
case 'TSMethodSignature': {
|
||||
const methodName = node.key.type === 'Identifier' ? node.key.name : typeToString(node.key);
|
||||
return `${methodName}()`;
|
||||
}
|
||||
case 'TSQualifiedName':
|
||||
return `${typeToString(node.left)}.${typeToString(node.right)}`;
|
||||
case 'TSParenthesizedType':
|
||||
return `(${typeToString(node.typeAnnotation)})`;
|
||||
case 'TSNonNullExpression':
|
||||
return `${typeToString(node.expression)}`;
|
||||
case 'TSTypeQuery':
|
||||
if (node.exprName.type === 'Identifier') {
|
||||
return `typeof ${node.exprName.name}`;
|
||||
}
|
||||
break;
|
||||
case 'TSMappedType': {
|
||||
const typeParam = node.typeParameter.name.constraint
|
||||
? `${node.typeParameter.name}: ${typeToString(node.typeParameter.constraint!)}`
|
||||
: node.typeParameter.name;
|
||||
const asClause = node.asClause
|
||||
? ` as ${typeToString(node.asClause)}`
|
||||
: '';
|
||||
return `{ [${typeParam}${asClause}]${node.optional ? '?' : ''} in ${typeToString(node.typeParameter.constraint || node.typeParameter)} }`;
|
||||
}
|
||||
case 'TSIndexedAccessType':
|
||||
return `${typeToString(node.objectType)}[${typeToString(node.indexType)}]`;
|
||||
case 'TSConditionalType':
|
||||
return `${typeToString(node.checkType)} extends ${typeToString(node.extendsType)} ? ${typeToString(node.trueType)} : ${typeToString(node.falseType)}`;
|
||||
case 'TSInferType':
|
||||
return `infer ${node.typeParameter.name}`;
|
||||
case 'TSTemplateLiteralType':
|
||||
return `\`${node.quasis.map((q: TSESTree.TemplateLiteral) => q.value.raw).join('')}\``;
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
export function isExternalModule(name: string): boolean {
|
||||
return name.startsWith('@') || !name.startsWith('.') && !name.startsWith('/');
|
||||
}
|
||||
|
||||
export function normalizeModuleName(moduleName: string, basePath: string): string {
|
||||
if (isExternalModule(moduleName)) {
|
||||
return moduleName;
|
||||
}
|
||||
return path.resolve(basePath, moduleName);
|
||||
}
|
||||
|
||||
import * as path from 'path';
|
||||
|
||||
export function getNodeId(node: TSESTree.Node, filePath: string): string {
|
||||
const name = getTypeName(node);
|
||||
if (name) {
|
||||
return `${filePath}#${name}`;
|
||||
}
|
||||
return `${filePath}#${node.range[0]}-${node.range[1]}`;
|
||||
}
|
||||
Reference in New Issue
Block a user