diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7531dd926ffc8..fab292e978d9d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13342,12 +13342,12 @@ namespace ts { case SyntaxKind.BinaryExpression: case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: - diagnostic = Diagnostics.Member_0_implicitly_has_an_1_type; + diagnostic = noImplicitAny ? Diagnostics.Member_0_implicitly_has_an_1_type : Diagnostics.Member_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage; break; case SyntaxKind.Parameter: diagnostic = (declaration).dotDotDotToken ? - Diagnostics.Rest_parameter_0_implicitly_has_an_any_type : - Diagnostics.Parameter_0_implicitly_has_an_1_type; + noImplicitAny ? Diagnostics.Rest_parameter_0_implicitly_has_an_any_type : Diagnostics.Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage : + noImplicitAny ? Diagnostics.Parameter_0_implicitly_has_an_1_type : Diagnostics.Parameter_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage; break; case SyntaxKind.BindingElement: diagnostic = Diagnostics.Binding_element_0_implicitly_has_an_1_type; @@ -13363,7 +13363,7 @@ namespace ts { error(declaration, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString); return; } - diagnostic = Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type; + diagnostic = noImplicitAny ? Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type : Diagnostics._0_implicitly_has_an_1_return_type_but_a_better_type_may_be_inferred_from_usage; break; case SyntaxKind.MappedType: if (noImplicitAny) { @@ -13371,7 +13371,7 @@ namespace ts { } return; default: - diagnostic = Diagnostics.Variable_0_implicitly_has_an_1_type; + diagnostic = noImplicitAny ? Diagnostics.Variable_0_implicitly_has_an_1_type : Diagnostics.Variable_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage; } errorOrSuggestion(noImplicitAny, declaration, diagnostic, declarationNameToString(getNameOfDeclaration(declaration)), typeAsString); } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 558c45bf2af13..37cab0359d48f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4041,6 +4041,39 @@ "category": "Error", "code": 7042 }, + "Variable '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7043 + }, + "Parameter '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7044 + }, + "Member '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7045 + }, + "Variable '{0}' implicitly has type '{1}' in some locations, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7046 + }, + "Rest parameter '{0}' implicitly has an 'any[]' type, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7047 + }, + "Property '{0}' implicitly has type 'any', but a better type for its get accessor may be inferred from usage.": { + "category": "Suggestion", + "code": 7048 + }, + "Property '{0}' implicitly has type 'any', but a better type for its set accessor may be inferred from usage.": { + "category": "Suggestion", + "code": 7049 + }, + "'{0}' implicitly has an '{1}' return type, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7050 + }, + "You cannot rename this element.": { "category": "Error", "code": 8000 diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index cef820bbf59ff..3a854084c0795 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -21,6 +21,27 @@ namespace ts.codefix { // Property declarations Diagnostics.Member_0_implicitly_has_an_1_type.code, + + //// Suggestions + // Variable declarations + Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_but_a_better_type_may_be_inferred_from_usage.code, + + // Variable uses + Diagnostics.Variable_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code, + + // Parameter declarations + Diagnostics.Parameter_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code, + Diagnostics.Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage.code, + + // Get Accessor declarations + Diagnostics.Property_0_implicitly_has_type_any_but_a_better_type_for_its_get_accessor_may_be_inferred_from_usage.code, + Diagnostics._0_implicitly_has_an_1_return_type_but_a_better_type_may_be_inferred_from_usage.code, + + // Set Accessor declarations + Diagnostics.Property_0_implicitly_has_type_any_but_a_better_type_for_its_set_accessor_may_be_inferred_from_usage.code, + + // Property declarations + Diagnostics.Member_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code, ]; registerCodeFix({ errorCodes, @@ -47,20 +68,46 @@ namespace ts.codefix { function getDiagnostic(errorCode: number, token: Node): DiagnosticMessage { switch (errorCode) { case Diagnostics.Parameter_0_implicitly_has_an_1_type.code: + case Diagnostics.Parameter_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code: return isSetAccessorDeclaration(getContainingFunction(token)!) ? Diagnostics.Infer_type_of_0_from_usage : Diagnostics.Infer_parameter_types_from_usage; // TODO: GH#18217 case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code: + case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage.code: return Diagnostics.Infer_parameter_types_from_usage; default: return Diagnostics.Infer_type_of_0_from_usage; } } + /** Map suggestion code to error code */ + function mapSuggestionDiagnostic(errorCode: number) { + switch (errorCode) { + case Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_but_a_better_type_may_be_inferred_from_usage.code: + return Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code; + case Diagnostics.Variable_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code: + return Diagnostics.Variable_0_implicitly_has_an_1_type.code; + case Diagnostics.Parameter_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code: + return Diagnostics.Parameter_0_implicitly_has_an_1_type.code; + case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage.code: + return Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code; + case Diagnostics.Property_0_implicitly_has_type_any_but_a_better_type_for_its_get_accessor_may_be_inferred_from_usage.code: + return Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation.code; + case Diagnostics._0_implicitly_has_an_1_return_type_but_a_better_type_may_be_inferred_from_usage.code: + return Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type.code; + case Diagnostics.Property_0_implicitly_has_type_any_but_a_better_type_for_its_set_accessor_may_be_inferred_from_usage.code: + return Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation.code; + case Diagnostics.Member_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage.code: + return Diagnostics.Member_0_implicitly_has_an_1_type.code; + } + return errorCode; + } + function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node, errorCode: number, program: Program, cancellationToken: CancellationToken, markSeen: NodeSeenTracker, host: LanguageServiceHost): Declaration | undefined { if (!isParameterPropertyModifier(token.kind) && token.kind !== SyntaxKind.Identifier && token.kind !== SyntaxKind.DotDotDotToken && token.kind !== SyntaxKind.ThisKeyword) { return undefined; } const { parent } = token; + errorCode = mapSuggestionDiagnostic(errorCode); switch (errorCode) { // Variable and Property declarations case Diagnostics.Member_0_implicitly_has_an_1_type.code: @@ -71,7 +118,7 @@ namespace ts.codefix { } if (isPropertyAccessExpression(parent)) { const type = inferTypeForVariableFromUsage(parent.name, program, cancellationToken); - const typeNode = type && getTypeNodeIfAccessible(type, parent, program, host); + const typeNode = getTypeNodeIfAccessible(type, parent, program, host); if (typeNode) { // Note that the codefix will never fire with an existing `@type` tag, so there is no need to merge tags const typeTag = createJSDocTypeTag(createJSDocTypeExpression(typeNode), /*comment*/ ""); @@ -107,7 +154,7 @@ namespace ts.codefix { case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code: if (markSeen(containingFunction)) { const param = cast(parent, isParameter); - annotateParameters(changes, param, containingFunction, sourceFile, program, host, cancellationToken); + annotateParameters(changes, sourceFile, param, containingFunction, program, host, cancellationToken); return param; } return undefined; @@ -152,7 +199,7 @@ namespace ts.codefix { return false; } - function annotateParameters(changes: textChanges.ChangeTracker, parameterDeclaration: ParameterDeclaration, containingFunction: FunctionLike, sourceFile: SourceFile, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken): void { + function annotateParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parameterDeclaration: ParameterDeclaration, containingFunction: FunctionLike, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken): void { if (!isIdentifier(parameterDeclaration.name) || !isApplicableFunctionForInference(containingFunction)) { return; } @@ -160,7 +207,7 @@ namespace ts.codefix { const parameterInferences = inferTypeForParametersFromUsage(containingFunction, sourceFile, program, cancellationToken) || containingFunction.parameters.map(p => ({ declaration: p, - type: isIdentifier(p.name) ? inferTypeForVariableFromUsage(p.name, program, cancellationToken) : undefined + type: isIdentifier(p.name) ? inferTypeForVariableFromUsage(p.name, program, cancellationToken) : program.getTypeChecker().getAnyType() })); Debug.assert(containingFunction.parameters.length === parameterInferences.length); @@ -179,8 +226,10 @@ namespace ts.codefix { function annotateSetAccessor(changes: textChanges.ChangeTracker, sourceFile: SourceFile, setAccessorDeclaration: SetAccessorDeclaration, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken): void { const param = firstOrUndefined(setAccessorDeclaration.parameters); if (param && isIdentifier(setAccessorDeclaration.name) && isIdentifier(param.name)) { - const type = inferTypeForVariableFromUsage(setAccessorDeclaration.name, program, cancellationToken) || - inferTypeForVariableFromUsage(param.name, program, cancellationToken); + let type = inferTypeForVariableFromUsage(setAccessorDeclaration.name, program, cancellationToken); + if (type === program.getTypeChecker().getAnyType()) { + type = inferTypeForVariableFromUsage(param.name, program, cancellationToken); + } if (isInJSFile(setAccessorDeclaration)) { annotateJSDocParameters(changes, sourceFile, [{ declaration: param, type }], program, host); } @@ -190,8 +239,8 @@ namespace ts.codefix { } } - function annotate(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: textChanges.TypeAnnotatable, type: Type | undefined, program: Program, host: LanguageServiceHost): void { - const typeNode = type && getTypeNodeIfAccessible(type, declaration, program, host); + function annotate(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: textChanges.TypeAnnotatable, type: Type, program: Program, host: LanguageServiceHost): void { + const typeNode = getTypeNodeIfAccessible(type, declaration, program, host); if (typeNode) { if (isInJSFile(sourceFile) && declaration.kind !== SyntaxKind.PropertySignature) { const parent = isVariableDeclaration(declaration) ? tryCast(declaration.parent.parent, isVariableStatement) : declaration; @@ -285,7 +334,7 @@ namespace ts.codefix { entry.kind !== FindAllReferences.EntryKind.Span ? tryCast(entry.node, isIdentifier) : undefined); } - function inferTypeForVariableFromUsage(token: Identifier, program: Program, cancellationToken: CancellationToken): Type | undefined { + function inferTypeForVariableFromUsage(token: Identifier, program: Program, cancellationToken: CancellationToken): Type { return InferFromReference.inferTypeFromReferences(getReferences(token, program, cancellationToken), program.getTypeChecker(), cancellationToken); } @@ -307,7 +356,7 @@ namespace ts.codefix { interface ParameterInference { readonly declaration: ParameterDeclaration; - readonly type?: Type; + readonly type: Type; readonly isOptional?: boolean; } @@ -329,13 +378,13 @@ namespace ts.codefix { stringIndexContext?: UsageContext; } - export function inferTypeFromReferences(references: ReadonlyArray, checker: TypeChecker, cancellationToken: CancellationToken): Type | undefined { + export function inferTypeFromReferences(references: ReadonlyArray, checker: TypeChecker, cancellationToken: CancellationToken): Type { const usageContext: UsageContext = {}; for (const reference of references) { cancellationToken.throwIfCancellationRequested(); inferTypeFromContext(reference, checker, usageContext); } - return getTypeFromUsageContext(usageContext, checker); + return getTypeFromUsageContext(usageContext, checker) || checker.getAnyType(); } export function inferTypeForParametersFromReferences(references: ReadonlyArray, declaration: FunctionLikeDeclaration, checker: TypeChecker, cancellationToken: CancellationToken): ParameterInference[] | undefined { @@ -374,7 +423,7 @@ namespace ts.codefix { } } if (!types.length) { - return { declaration: parameter }; + return { declaration: parameter, type: checker.getAnyType() }; } const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype)); return { diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts index 5e18925fdfee0..f57c1a8101bd6 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts @@ -6,10 +6,11 @@ verify.getSuggestionDiagnostics([ { message: "JSDoc types may be moved to TypeScript types.", code: 80004 }, - { message: "Variable 'x' implicitly has an 'any' type.", code: 7005 }]); + { message: "Variable 'x' implicitly has an 'any' type, but a better type may be inferred from usage.", code: 7043 }]); verify.codeFix({ description: "Annotate with type from JSDoc", + index: 0, newFileContent: `/** @type {number} */ var x: number;`, diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc10.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc10.ts index 2bd1df5c6e6b3..98cc31e32075b 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc10.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc10.ts @@ -8,6 +8,7 @@ verify.codeFix({ description: "Annotate with type from JSDoc", + index: 0, newFileContent: `/** * @param {?} x diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc12.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc12.ts index 2152355df8b9e..ec1ab37022214 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc12.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc12.ts @@ -11,6 +11,7 @@ verify.codeFix({ description: "Annotate with type from JSDoc", + index: 0, newFileContent: `class C { /** diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc15.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc15.ts index 9d26eaa8cdeb0..b278b50d6e1e1 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc15.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc15.ts @@ -17,6 +17,7 @@ verify.codeFix({ description: "Annotate with type from JSDoc", + index: 9, newFileContent: `/** * @param {Boolean} x diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc17.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc17.ts index a4453da882551..c452dd4516f20 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc17.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc17.ts @@ -9,6 +9,7 @@ verify.codeFix({ description: "Annotate with type from JSDoc", + index: 0, newFileContent: `class C { /** diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc18.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc18.ts index 685287f209283..9fc2e966028aa 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc18.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc18.ts @@ -6,6 +6,7 @@ verify.codeFix({ description: "Annotate with type from JSDoc", + index: 0, newFileContent: `class C { /** @param {number} value */ diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc19.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc19.ts index 754d29d0e46c9..cdb057d8baa42 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc19.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc19.ts @@ -11,6 +11,7 @@ verify.codeFix({ description: "Annotate with type from JSDoc", + index: 2, newFileContent: `/** * @template T diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts index 9163805bfcfdd..98ccf84c3d677 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts @@ -9,6 +9,7 @@ verify.codeFix({ description: "Annotate with type from JSDoc", + index: 2, newFileContent: ` /** @param {Object} sb diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts index 7e064f8dae46e..f7d0522018abd 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts @@ -13,13 +13,14 @@ const [r0, r1, r2, r3, r4] = test.ranges(); verify.getSuggestionDiagnostics([ {message: "JSDoc types may be moved to TypeScript types.", code: 80004, range: r0}, - {message: "Parameter 'x' implicitly has an 'any' type.", code: 7006, range: r1 }, - {message: "Parameter 'y' implicitly has an 'any' type.", code: 7006, range: r2 }, - {message: "Parameter 'alpha' implicitly has an 'any' type.", code: 7006, range: r3 }, - {message: "Parameter 'beta' implicitly has an 'any' type.", code: 7006, range: r4 }]); + {message: "Parameter 'x' implicitly has an 'any' type, but a better type may be inferred from usage.", code: 7044, range: r1 }, + {message: "Parameter 'y' implicitly has an 'any' type, but a better type may be inferred from usage.", code: 7044, range: r2 }, + {message: "Parameter 'alpha' implicitly has an 'any' type, but a better type may be inferred from usage.", code: 7044, range: r3 }, + {message: "Parameter 'beta' implicitly has an 'any' type, but a better type may be inferred from usage.", code: 7044, range: r4 }]); verify.codeFix({ description: "Annotate with type from JSDoc", + index: 0, newFileContent: // TODO: GH#22358 `/** diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts index c6010bf6cd002..4421afbd86777 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts @@ -15,6 +15,7 @@ verify.codeFix({ description: "Annotate with type from JSDoc", + index: 7, newFileContent: `/** * @param {*} x diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc6.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc6.ts index 9d11eb8d4457a..dbb75590fb2ba 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc6.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc6.ts @@ -7,6 +7,7 @@ verify.codeFix({ description: "Annotate with type from JSDoc", + index: 0, newFileContent: `declare class C { /** @type {number | null} */ diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc7.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc7.ts index ba3bd7d47a0b4..0d43bde45d49b 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc7.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc7.ts @@ -10,6 +10,7 @@ verify.codeFix({ description: "Annotate with type from JSDoc", + index: 0, newFileContent: `/** * @param {number} x diff --git a/tests/cases/fourslash/codeFixConvertToMappedObjectType12.ts b/tests/cases/fourslash/codeFixConvertToMappedObjectType12.ts index 6374dc43dacb4..d4014463ab2c1 100644 --- a/tests/cases/fourslash/codeFixConvertToMappedObjectType12.ts +++ b/tests/cases/fourslash/codeFixConvertToMappedObjectType12.ts @@ -9,4 +9,6 @@ //// readonly [prop: K]?: any; //// } -verify.not.codeFixAvailable() +verify.codeFixAvailable([ + { "description": "Infer type of 'any' from usage" } +]) diff --git a/tests/cases/fourslash/codeFixForgottenThisPropertyAccess02.ts b/tests/cases/fourslash/codeFixForgottenThisPropertyAccess02.ts index 60d15fb259c02..6f6c6f5510d41 100644 --- a/tests/cases/fourslash/codeFixForgottenThisPropertyAccess02.ts +++ b/tests/cases/fourslash/codeFixForgottenThisPropertyAccess02.ts @@ -8,5 +8,6 @@ verify.codeFix({ description: "Add 'this.' to unresolved variable", + index: 0, newRangeContent: "this.foo = 10", }); diff --git a/tests/cases/fourslash/codeFixInferFromUsageAlwaysInfer.ts b/tests/cases/fourslash/codeFixInferFromUsageAlwaysInfer.ts new file mode 100644 index 0000000000000..7e83eb68fbee6 --- /dev/null +++ b/tests/cases/fourslash/codeFixInferFromUsageAlwaysInfer.ts @@ -0,0 +1,11 @@ +/// +// @noImplicitAny: false +////function coll(callback) { +////} + verify.codeFix({ + description: "Infer parameter types from usage", + index: 0, + newFileContent: +`function coll(callback: any) { +}`, +}); diff --git a/tests/cases/fourslash/codeFixInferFromUsageAlwaysInferJS.ts b/tests/cases/fourslash/codeFixInferFromUsageAlwaysInferJS.ts new file mode 100644 index 0000000000000..53482451d843c --- /dev/null +++ b/tests/cases/fourslash/codeFixInferFromUsageAlwaysInferJS.ts @@ -0,0 +1,18 @@ +/// +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @noImplicitAny: false +// @Filename: important.js +////function coll(callback) { +////} + verify.codeFix({ + description: "Infer parameter types from usage", + index: 0, + newFileContent: +`/** + * @param {any} callback + */ +function coll(callback) { +}`, +}); diff --git a/tests/cases/fourslash/codeFixInferFromUsageVariable2.ts b/tests/cases/fourslash/codeFixInferFromUsageVariable2.ts index bf8e2bb07e54d..6034adef2d760 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageVariable2.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageVariable2.ts @@ -10,4 +10,4 @@ verify.rangeAfterCodeFix(`var x: number; function f() { x++; } -`, /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 1); \ No newline at end of file +`, /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 1); diff --git a/tests/cases/fourslash/codeFixInferFromUsageVariable3.ts b/tests/cases/fourslash/codeFixInferFromUsageVariable3.ts new file mode 100644 index 0000000000000..390d8c962fb6d --- /dev/null +++ b/tests/cases/fourslash/codeFixInferFromUsageVariable3.ts @@ -0,0 +1,13 @@ +/// + +// @noImplicitAny: false +////[|var x; +////function f() { +//// x++; +////}|] + +verify.rangeAfterCodeFix(`var x: number; +function f() { + x++; +} +`, /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/codeFixInferFromUsageVariable3JS.ts b/tests/cases/fourslash/codeFixInferFromUsageVariable3JS.ts new file mode 100644 index 0000000000000..83a40c396b078 --- /dev/null +++ b/tests/cases/fourslash/codeFixInferFromUsageVariable3JS.ts @@ -0,0 +1,22 @@ +/// + +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @noImplicitAny: false +// @Filename: important.js + +////[|function f(foo) { +//// foo += 2 +//// return foo +////}|] + + +verify.rangeAfterCodeFix(`/** + * @param {number} foo + */ +function f(foo) { + foo += 2 + return foo +} +`); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_suggestion.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_suggestion.ts index a456299c68aeb..9f9eb992adeb0 100644 --- a/tests/cases/fourslash/codeFixUnusedIdentifier_suggestion.ts +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_suggestion.ts @@ -7,9 +7,9 @@ const [r0, r1] = test.ranges(); verify.getSuggestionDiagnostics([ { - message: "Parameter 'p' implicitly has an 'any' type.", + message: "Parameter 'p' implicitly has an 'any' type, but a better type may be inferred from usage.", range: r0, - code: 7006, + code: 7044, }, { message: "'p' is declared but its value is never read.", @@ -26,5 +26,9 @@ verify.getSuggestionDiagnostics([ ]); verify.codeFixAvailable( - ["Remove declaration for: 'p'", "Prefix 'p' with an underscore", "Remove declaration for: 'x'"] - .map(description => ({ description }))); + [ + "Infer parameter types from usage", + "Remove declaration for: 'p'", + "Prefix 'p' with an underscore", + "Remove declaration for: 'x'" + ].map(description => ({ description }))); diff --git a/tests/cases/fourslash/incompleteFunctionCallCodefix.ts b/tests/cases/fourslash/incompleteFunctionCallCodefix.ts index 9935ee854f38d..c501a450204ce 100644 --- a/tests/cases/fourslash/incompleteFunctionCallCodefix.ts +++ b/tests/cases/fourslash/incompleteFunctionCallCodefix.ts @@ -6,4 +6,7 @@ ////} ////f( -verify.not.codeFixAvailable(); +goTo.marker('1'); +verify.codeFixAvailable([ + { "description": "Infer parameter types from usage" } +]); diff --git a/tests/cases/fourslash/incompleteFunctionCallCodefix2.ts b/tests/cases/fourslash/incompleteFunctionCallCodefix2.ts index bfea758b2da8e..540cb0c626af2 100644 --- a/tests/cases/fourslash/incompleteFunctionCallCodefix2.ts +++ b/tests/cases/fourslash/incompleteFunctionCallCodefix2.ts @@ -3,4 +3,6 @@ // @noImplicitAny: true ////function f(new C(100, 3, undefined) -verify.codeFixAvailable([]); // Parse error, so no unused diagnostics +verify.codeFixAvailable([ + { "description": "Infer parameter types from usage" } +]); // Parse error, so no unused diagnostics diff --git a/tests/cases/fourslash/noSuggestionDiagnosticsOnParseError.ts b/tests/cases/fourslash/noSuggestionDiagnosticsOnParseError.ts index f992f4c8e8e10..c36d5352e7fd7 100644 --- a/tests/cases/fourslash/noSuggestionDiagnosticsOnParseError.ts +++ b/tests/cases/fourslash/noSuggestionDiagnosticsOnParseError.ts @@ -6,8 +6,8 @@ // Only give suggestions for nodes that do NOT have parse errors verify.getSuggestionDiagnostics([{ - message: "Variable 'd' implicitly has an 'any' type.", - code: 7005, + message: "Variable 'd' implicitly has an 'any' type, but a better type may be inferred from usage.", + code: 7043, range: { fileName: "/a.ts", pos: 23, diff --git a/tests/cases/fourslash/unusedClassInNamespace2.ts b/tests/cases/fourslash/unusedClassInNamespace2.ts index 0b6b7504633ec..0f87f9f293817 100644 --- a/tests/cases/fourslash/unusedClassInNamespace2.ts +++ b/tests/cases/fourslash/unusedClassInNamespace2.ts @@ -11,5 +11,5 @@ verify.rangeAfterCodeFix(`namespace greeter { export class class2 { } -}`); +}`, /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/unusedLocalsInFunction4.ts b/tests/cases/fourslash/unusedLocalsInFunction4.ts index 62d128ea9e71b..2c511f456d75b 100644 --- a/tests/cases/fourslash/unusedLocalsInFunction4.ts +++ b/tests/cases/fourslash/unusedLocalsInFunction4.ts @@ -6,4 +6,4 @@ //// use(y, z); ////} -verify.rangeAfterCodeFix("var y = 0,z = 1;"); +verify.rangeAfterCodeFix("var y = 0,z = 1;", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/unusedLocalsInMethodFS1.ts b/tests/cases/fourslash/unusedLocalsInMethodFS1.ts index ecfb745527637..1eb261ee7770d 100644 --- a/tests/cases/fourslash/unusedLocalsInMethodFS1.ts +++ b/tests/cases/fourslash/unusedLocalsInMethodFS1.ts @@ -9,4 +9,4 @@ //// } ////} -verify.rangeAfterCodeFix("var y = 10;"); +verify.rangeAfterCodeFix("var y = 10;", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/unusedLocalsInMethodFS2.ts b/tests/cases/fourslash/unusedLocalsInMethodFS2.ts index d06e803bf7fa3..095e80e9fe963 100644 --- a/tests/cases/fourslash/unusedLocalsInMethodFS2.ts +++ b/tests/cases/fourslash/unusedLocalsInMethodFS2.ts @@ -9,4 +9,4 @@ //// } ////} -verify.rangeAfterCodeFix("var y;"); +verify.rangeAfterCodeFix("var y;", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/unusedVariableInClass2.ts b/tests/cases/fourslash/unusedVariableInClass2.ts index 2e6b382698c95..1fde857253a83 100644 --- a/tests/cases/fourslash/unusedVariableInClass2.ts +++ b/tests/cases/fourslash/unusedVariableInClass2.ts @@ -8,5 +8,6 @@ verify.codeFix({ description: "Remove declaration for: 'greeting'", + index: 0, newRangeContent: "public greeting1;\n", }); diff --git a/tests/cases/fourslash/unusedVariableInNamespace2.ts b/tests/cases/fourslash/unusedVariableInNamespace2.ts index 2719fea1b21c1..6123555fc8a85 100644 --- a/tests/cases/fourslash/unusedVariableInNamespace2.ts +++ b/tests/cases/fourslash/unusedVariableInNamespace2.ts @@ -10,5 +10,6 @@ verify.codeFix({ description: "Remove declaration for: 'b'", + index: 0, newRangeContent: 'let a = "dummy entry", c = 0;', }); diff --git a/tests/cases/fourslash/unusedVariableInNamespace3.ts b/tests/cases/fourslash/unusedVariableInNamespace3.ts index a542b5314105a..3503bae4e6a34 100644 --- a/tests/cases/fourslash/unusedVariableInNamespace3.ts +++ b/tests/cases/fourslash/unusedVariableInNamespace3.ts @@ -10,5 +10,6 @@ verify.codeFix({ description: "Remove declaration for: 'c'", + index: 0, newRangeContent: 'let a = "dummy entry", b;', });