mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
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:
@@ -1641,6 +1641,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
getBaseTypeOfLiteralType,
|
||||
getWidenedType,
|
||||
getWidenedLiteralType,
|
||||
fillMissingTypeArguments,
|
||||
getTypeFromTypeNode: nodeIn => {
|
||||
const node = getParseTreeNode(nodeIn, isTypeNode);
|
||||
return node ? getTypeFromTypeNode(node) : errorType;
|
||||
|
||||
@@ -5421,6 +5421,7 @@ export interface TypeChecker {
|
||||
/** @internal */ isTypeParameterPossiblyReferenced(tp: TypeParameter, node: Node): boolean;
|
||||
/** @internal */ typeHasCallOrConstructSignatures(type: Type): boolean;
|
||||
/** @internal */ getSymbolFlags(symbol: Symbol): SymbolFlags;
|
||||
/** @internal */ fillMissingTypeArguments(typeArguments: readonly Type[], typeParameters: readonly TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScriptImplicitAny: boolean): Type[];
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// @isolatedDeclarations: true
|
||||
// @declaration: true
|
||||
// @lib: es2015
|
||||
////let x: Iterator<number>;
|
||||
////export const y = x;
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add annotation of type 'Iterator<number>'",
|
||||
index: 0,
|
||||
newFileContent:
|
||||
`let x: Iterator<number>;
|
||||
export const y: Iterator<number> = x;`,
|
||||
});
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// @isolatedDeclarations: true
|
||||
// @declaration: true
|
||||
|
||||
////export interface Foo<T, U = T[]> {}
|
||||
////export function foo(x: Foo<string>) {
|
||||
//// return x;
|
||||
////}
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add return type 'Foo<string>'",
|
||||
index: 0,
|
||||
newFileContent:
|
||||
`export interface Foo<T, U = T[]> {}
|
||||
export function foo(x: Foo<string>): Foo<string> {
|
||||
return x;
|
||||
}`,
|
||||
});
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// In the abstract, we might prefer the inferred return type annotation to
|
||||
// be identical to the parameter type (with 2 type parameters).
|
||||
// Our current heuristic to avoid overly complex types in this case creates
|
||||
// "overly simple" types, but this tradeoff seems reasonable.
|
||||
|
||||
// @isolatedDeclarations: true
|
||||
// @declaration: true
|
||||
////export interface Foo<T, U = T[]> {}
|
||||
////export function foo(x: Foo<string, string[]>) {
|
||||
//// return x;
|
||||
////}
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add return type 'Foo<string>'",
|
||||
index: 0,
|
||||
newFileContent:
|
||||
`export interface Foo<T, U = T[]> {}
|
||||
export function foo(x: Foo<string, string[]>): Foo<string> {
|
||||
return x;
|
||||
}`,
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// Our current heursitic to avoid overly verbose generic types
|
||||
// doesn't handle generic types nested inside other types.
|
||||
|
||||
// @isolatedDeclarations: true
|
||||
// @declaration: true
|
||||
////export interface Foo<T, U = T[]> {}
|
||||
////export function foo(x: Map<number, Foo<string>>) {
|
||||
//// return x;
|
||||
////}
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add return type 'Map<number, Foo<string, string[]>>'",
|
||||
index: 0,
|
||||
newFileContent:
|
||||
`export interface Foo<T, U = T[]> {}
|
||||
export function foo(x: Map<number, Foo<string>>): Map<number, Foo<string, string[]>> {
|
||||
return x;
|
||||
}`,
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// @isolatedDeclarations: true
|
||||
// @declaration: true
|
||||
// @lib: es2015
|
||||
//// export function foo(x: Generator<number>) {
|
||||
//// return x;
|
||||
//// }
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add return type 'Generator<number>'",
|
||||
index: 0,
|
||||
newFileContent:
|
||||
`export function foo(x: Generator<number>): Generator<number> {
|
||||
return x;
|
||||
}`
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// @isolatedDeclarations: true
|
||||
// @declaration: true
|
||||
// @lib: es2015
|
||||
//// export function *foo() {
|
||||
//// yield 5;
|
||||
//// }
|
||||
|
||||
verify.codeFix({
|
||||
description: "Add return type 'Generator<number, void, unknown>'",
|
||||
index: 0,
|
||||
newFileContent:
|
||||
`export function *foo(): Generator<number, void, unknown> {
|
||||
yield 5;
|
||||
}`
|
||||
});
|
||||
Reference in New Issue
Block a user