Drop unnecessary type arguments in the isolated declarations quick fix (#59665)

Co-authored-by: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
Ben Lickly
2024-09-16 10:35:50 -07:00
committed by GitHub
parent 52eaa7b02f
commit 8230bc66a7
10 changed files with 167 additions and 4 deletions

View File

@@ -4,8 +4,9 @@ import {
createImportAdder,
eachDiagnostic,
registerCodeFix,
typeNodeToAutoImportableTypeNode,
typePredicateToAutoImportableTypeNode,
typeToAutoImportableTypeNode,
typeToMinimizedReferenceType,
} from "../_namespaces/ts.codefix.js";
import {
ArrayBindingPattern,
@@ -1096,9 +1097,9 @@ function withContext<T>(
return emptyInferenceResult;
}
function typeToTypeNode(type: Type, enclosingDeclaration: Node, flags = NodeBuilderFlags.None) {
function typeToTypeNode(type: Type, enclosingDeclaration: Node, flags = NodeBuilderFlags.None): TypeNode | undefined {
let isTruncated = false;
const result = typeToAutoImportableTypeNode(typeChecker, importAdder, type, enclosingDeclaration, scriptTarget, declarationEmitNodeBuilderFlags | flags, declarationEmitInternalNodeBuilderFlags, {
const minimizedTypeNode = typeToMinimizedReferenceType(typeChecker, type, enclosingDeclaration, declarationEmitNodeBuilderFlags | flags, declarationEmitInternalNodeBuilderFlags, {
moduleResolverHost: program,
trackSymbol() {
return true;
@@ -1107,6 +1108,10 @@ function withContext<T>(
isTruncated = true;
},
});
if (!minimizedTypeNode) {
return undefined;
}
const result = typeNodeToAutoImportableTypeNode(minimizedTypeNode, importAdder, scriptTarget);
return isTruncated ? factory.createKeywordTypeNode(SyntaxKind.AnyKeyword) : result;
}

View File

@@ -23,6 +23,7 @@ import {
flatMap,
FunctionDeclaration,
FunctionExpression,
GenericType,
GetAccessorDeclaration,
getAllAccessorDeclarations,
getCheckFlags,
@@ -59,6 +60,7 @@ import {
isSetAccessorDeclaration,
isStringLiteral,
isTypeNode,
isTypeReferenceNode,
isTypeUsableAsPropertyName,
isYieldExpression,
LanguageServiceHost,
@@ -595,7 +597,15 @@ function createTypeParameterName(index: number) {
/** @internal */
export function typeToAutoImportableTypeNode(checker: TypeChecker, importAdder: ImportAdder, type: Type, contextNode: Node | undefined, scriptTarget: ScriptTarget, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker): TypeNode | undefined {
let typeNode = checker.typeToTypeNode(type, contextNode, flags, internalFlags, tracker);
const typeNode = checker.typeToTypeNode(type, contextNode, flags, internalFlags, tracker);
if (!typeNode) {
return undefined;
}
return typeNodeToAutoImportableTypeNode(typeNode, importAdder, scriptTarget);
}
/** @internal */
export function typeNodeToAutoImportableTypeNode(typeNode: TypeNode, importAdder: ImportAdder, scriptTarget: ScriptTarget): TypeNode | undefined {
if (typeNode && isImportTypeNode(typeNode)) {
const importableReference = tryGetAutoImportableReferenceFromTypeNode(typeNode, scriptTarget);
if (importableReference) {
@@ -608,6 +618,40 @@ export function typeToAutoImportableTypeNode(checker: TypeChecker, importAdder:
return getSynthesizedDeepClone(typeNode);
}
function endOfRequiredTypeParameters(checker: TypeChecker, type: GenericType): number {
Debug.assert(type.typeArguments);
const fullTypeArguments = type.typeArguments;
const target = type.target;
for (let cutoff = 0; cutoff < fullTypeArguments.length; cutoff++) {
const typeArguments = fullTypeArguments.slice(0, cutoff);
const filledIn = checker.fillMissingTypeArguments(typeArguments, target.typeParameters, cutoff, /*isJavaScriptImplicitAny*/ false);
if (filledIn.every((fill, i) => fill === fullTypeArguments[i])) {
return cutoff;
}
}
// If we make it all the way here, all the type arguments are required.
return fullTypeArguments.length;
}
/** @internal */
export function typeToMinimizedReferenceType(checker: TypeChecker, type: Type, contextNode: Node | undefined, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker): TypeNode | undefined {
let typeNode = checker.typeToTypeNode(type, contextNode, flags, internalFlags, tracker);
if (!typeNode) {
return undefined;
}
if (isTypeReferenceNode(typeNode)) {
const genericType = type as GenericType;
if (genericType.typeArguments && typeNode.typeArguments) {
const cutoff = endOfRequiredTypeParameters(checker, genericType);
if (cutoff < typeNode.typeArguments.length) {
const newTypeArguments = factory.createNodeArray(typeNode.typeArguments.slice(0, cutoff));
typeNode = factory.updateTypeReferenceNode(typeNode, typeNode.typeName, newTypeArguments);
}
}
}
return typeNode;
}
/** @internal */
export function typePredicateToAutoImportableTypeNode(checker: TypeChecker, importAdder: ImportAdder, typePredicate: TypePredicate, contextNode: Node | undefined, scriptTarget: ScriptTarget, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker): TypeNode | undefined {
let typePredicateNode = checker.typePredicateToTypePredicateNode(typePredicate, contextNode, flags, internalFlags, tracker);