-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Drop unnecessary type arguments in the isolated declarations quick fix #59665
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
dd29423
6f8e37d
b0bc68f
de61d6d
b0b1e02
943761b
d53552f
77ac42f
71c092a
14deb8e
a603aa2
4e21657
cd0cd4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ import { | |
| flatMap, | ||
| FunctionDeclaration, | ||
| FunctionExpression, | ||
| GenericType, | ||
| GetAccessorDeclaration, | ||
| getAllAccessorDeclarations, | ||
| getCheckFlags, | ||
|
|
@@ -59,6 +60,8 @@ import { | |
| isSetAccessorDeclaration, | ||
| isStringLiteral, | ||
| isTypeNode, | ||
| isTypeReferenceNode, | ||
| TypeReferenceNode, | ||
| isTypeUsableAsPropertyName, | ||
| isYieldExpression, | ||
| LanguageServiceHost, | ||
|
|
@@ -595,7 +598,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 +619,43 @@ export function typeToAutoImportableTypeNode(checker: TypeChecker, importAdder: | |
| return getSynthesizedDeepClone(typeNode); | ||
| } | ||
|
|
||
| function endOfRequiredTypeParameters(checker: TypeChecker, type: GenericType, fullTypeArguments: readonly Type[]) : number { | ||
| if (fullTypeArguments !== type.typeArguments!) { | ||
| throw new Error('fullTypeArguments should be set') | ||
|
jakebailey marked this conversation as resolved.
Outdated
|
||
| } | ||
| const target = type.target; | ||
| next_cutoff: for (let cutoff = 0; cutoff < fullTypeArguments.length; cutoff++) { | ||
| const typeArguments = fullTypeArguments.slice(0, cutoff); | ||
| const filledIn = checker.fillMissingTypeArguments(typeArguments, target.typeParameters, cutoff, /*isJavaScriptImplicitAny*/ false); | ||
| for (let i = 0; i < filledIn.length; i++) { | ||
| // If they don't match, then we haven't yet reached the right cutoff | ||
| if (filledIn[i] !== fullTypeArguments[i]) continue next_cutoff; | ||
| } | ||
| return cutoff; | ||
|
blickly marked this conversation as resolved.
Outdated
|
||
| } | ||
| // If we make it all the way here, all the type arguments are required. | ||
| return fullTypeArguments.length; | ||
| } | ||
|
|
||
| export function typeToMinimizedReferenceType(checker: TypeChecker, type: Type, contextNode: Node | undefined, flags?: NodeBuilderFlags, internalFlags?: InternalNodeBuilderFlags, tracker?: SymbolTracker): TypeNode | undefined { | ||
| const typeNode = checker.typeToTypeNode(type, contextNode, flags, internalFlags, tracker); | ||
| if (!typeNode) { | ||
| return undefined; | ||
| } | ||
| if (isTypeReferenceNode(typeNode)) { | ||
| const genericType = type as GenericType; | ||
| if (genericType.typeArguments) { | ||
| const cutoff = endOfRequiredTypeParameters(checker, genericType, genericType.typeArguments); | ||
| if (cutoff !== undefined && typeNode.typeArguments) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. Done. |
||
| // Looks like the wrong way to do this. What APIs should I use here? | ||
| (typeNode as any).typeArguments = typeNode.typeArguments.slice(0, cutoff); | ||
|
jakebailey marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
|
|
||
| } | ||
| 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); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| /// <reference path='fourslash.ts'/> | ||
|
|
||
| // @isolatedDeclarations: true | ||
| // @declaration: true | ||
| // @file lib.d.ts | ||
| ////interface MyIterator<T, TReturn = any, TNext = undefined> {} | ||
| ////let x: MyIterator<number>; | ||
| ////export const y = x; | ||
|
|
||
| verify.codeFix({ | ||
| description: "Add annotation of type 'MyIterator<number>'", | ||
| index: 0, | ||
| newFileContent: | ||
| `interface MyIterator<T, TReturn = any, TNext = undefined> {} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have tests for generators elsewhere? I seem to recall us auto-adding type long type annotations for those...
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I can add a case with Generator. For reasons I don't fully understand, The special casing inside
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That was a part of #58243.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. Thanks for the context. |
||
| let x: MyIterator<number>; | ||
| export const y: MyIterator<number> = x;`, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| }`, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| /// <reference path='fourslash.ts'/> | ||
|
sandersn marked this conversation as resolved.
|
||
|
|
||
| // In the abstract, we might prefer the inferred return type annotation to | ||
| // be identical to the parameter type (with 2 type parameters). | ||
| // Our current heursitic to avoid overly complex types in this case creates | ||
|
blickly marked this conversation as resolved.
Outdated
|
||
| // "overly simple" types, but this tradeoff seems reasonable. | ||
|
sandersn marked this conversation as resolved.
|
||
|
|
||
| // @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; | ||
| }`, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| }`, | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.