Skip to content

Commit ec0e8cb

Browse files
authored
noImplicitAny as suggestion (#27693)
* noImplicitAny as suggestion Note that not all noImplicitAny errors turn into suggestions. In particular, 1. reportErrorsFromWidening does not, because it doesn't log an error that infer-from-usage fixes, and fixing it would require otherwise-unnecessary code. 2. auto types do not have implicit any suggestions, because that would require running control flow in noImplicitAny mode. * Rename reportImplicitAny+forbid it for non-checkJS In JS, you only get implicit any errors/suggestions with checkJS or ts-check turned on. * Update baselines * Use isCheckJsEnabledForFile * Remove noImplicitAny parameter since it's in scope already
1 parent 99b2718 commit ec0e8cb

8 files changed

+61
-49
lines changed

Diff for: src/compiler/checker.ts

+29-33
Original file line numberDiff line numberDiff line change
@@ -4915,9 +4915,7 @@ namespace ts {
49154915
}
49164916
const widened = getWidenedType(addOptionality(type, definedInMethod && !definedInConstructor));
49174917
if (filterType(widened, t => !!(t.flags & ~TypeFlags.Nullable)) === neverType) {
4918-
if (noImplicitAny) {
4919-
reportImplicitAnyError(symbol.valueDeclaration, anyType);
4920-
}
4918+
reportImplicitAny(symbol.valueDeclaration, anyType);
49214919
return anyType;
49224920
}
49234921
return widened;
@@ -4992,9 +4990,7 @@ namespace ts {
49924990
return result;
49934991
}
49944992
if (isEmptyArrayLiteralType(type)) {
4995-
if (noImplicitAny) {
4996-
reportImplicitAnyError(expression, anyArrayType);
4997-
}
4993+
reportImplicitAny(expression, anyArrayType);
49984994
return anyArrayType;
49994995
}
50004996
return type;
@@ -5044,8 +5040,8 @@ namespace ts {
50445040
if (isBindingPattern(element.name)) {
50455041
return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors);
50465042
}
5047-
if (reportErrors && noImplicitAny && !declarationBelongsToPrivateAmbientMember(element)) {
5048-
reportImplicitAnyError(element, anyType);
5043+
if (reportErrors && !declarationBelongsToPrivateAmbientMember(element)) {
5044+
reportImplicitAny(element, anyType);
50495045
}
50505046
return anyType;
50515047
}
@@ -5145,9 +5141,9 @@ namespace ts {
51455141
type = isParameter(declaration) && declaration.dotDotDotToken ? anyArrayType : anyType;
51465142

51475143
// Report implicit any errors unless this is a private property within an ambient declaration
5148-
if (reportErrors && noImplicitAny) {
5144+
if (reportErrors) {
51495145
if (!declarationBelongsToPrivateAmbientMember(declaration)) {
5150-
reportImplicitAnyError(declaration, type);
5146+
reportImplicitAny(declaration, type);
51515147
}
51525148
}
51535149
return type;
@@ -5328,14 +5324,12 @@ namespace ts {
53285324
}
53295325
// Otherwise, fall back to 'any'.
53305326
else {
5331-
if (noImplicitAny) {
5332-
if (setter) {
5333-
error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol));
5334-
}
5335-
else {
5336-
Debug.assert(!!getter, "there must existed getter as we are current checking either setter or getter in this function");
5337-
error(getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol));
5338-
}
5327+
if (setter) {
5328+
errorOrSuggestion(noImplicitAny, setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol));
5329+
}
5330+
else {
5331+
Debug.assert(!!getter, "there must existed getter as we are current checking either setter or getter in this function");
5332+
errorOrSuggestion(noImplicitAny, getter!, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol));
53395333
}
53405334
type = anyType;
53415335
}
@@ -13282,8 +13276,12 @@ namespace ts {
1328213276
return errorReported;
1328313277
}
1328413278

13285-
function reportImplicitAnyError(declaration: Declaration, type: Type) {
13279+
function reportImplicitAny(declaration: Declaration, type: Type) {
1328613280
const typeAsString = typeToString(getWidenedType(type));
13281+
if (isInJSFile(declaration) && !isCheckJsEnabledForFile(getSourceFileOfNode(declaration), compilerOptions)) {
13282+
// Only report implicit any errors/suggestions in TS and ts-check JS files
13283+
return;
13284+
}
1328713285
let diagnostic: DiagnosticMessage;
1328813286
switch (declaration.kind) {
1328913287
case SyntaxKind.BinaryExpression:
@@ -13306,26 +13304,28 @@ namespace ts {
1330613304
case SyntaxKind.SetAccessor:
1330713305
case SyntaxKind.FunctionExpression:
1330813306
case SyntaxKind.ArrowFunction:
13309-
if (!(declaration as NamedDeclaration).name) {
13307+
if (noImplicitAny && !(declaration as NamedDeclaration).name) {
1331013308
error(declaration, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString);
1331113309
return;
1331213310
}
1331313311
diagnostic = Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type;
1331413312
break;
1331513313
case SyntaxKind.MappedType:
13316-
error(declaration, Diagnostics.Mapped_object_type_implicitly_has_an_any_template_type);
13314+
if (noImplicitAny) {
13315+
error(declaration, Diagnostics.Mapped_object_type_implicitly_has_an_any_template_type);
13316+
}
1331713317
return;
1331813318
default:
1331913319
diagnostic = Diagnostics.Variable_0_implicitly_has_an_1_type;
1332013320
}
13321-
error(declaration, diagnostic, declarationNameToString(getNameOfDeclaration(declaration)), typeAsString);
13321+
errorOrSuggestion(noImplicitAny, declaration, diagnostic, declarationNameToString(getNameOfDeclaration(declaration)), typeAsString);
1332213322
}
1332313323

1332413324
function reportErrorsFromWidening(declaration: Declaration, type: Type) {
1332513325
if (produceDiagnostics && noImplicitAny && type.flags & TypeFlags.ContainsWideningType) {
1332613326
// Report implicit any error within type if possible, otherwise report error on declaration
1332713327
if (!reportWideningErrorsInType(type)) {
13328-
reportImplicitAnyError(declaration, type);
13328+
reportImplicitAny(declaration, type);
1332913329
}
1333013330
}
1333113331
}
@@ -22319,15 +22319,11 @@ namespace ts {
2231922319
isTypeAssertion(initializer) ? type : getWidenedLiteralType(type);
2232022320
if (isInJSFile(declaration)) {
2232122321
if (widened.flags & TypeFlags.Nullable) {
22322-
if (noImplicitAny) {
22323-
reportImplicitAnyError(declaration, anyType);
22324-
}
22322+
reportImplicitAny(declaration, anyType);
2232522323
return anyType;
2232622324
}
2232722325
else if (isEmptyArrayLiteralType(widened)) {
22328-
if (noImplicitAny) {
22329-
reportImplicitAnyError(declaration, anyArrayType);
22330-
}
22326+
reportImplicitAny(declaration, anyArrayType);
2233122327
return anyArrayType;
2233222328
}
2233322329
}
@@ -23318,8 +23314,8 @@ namespace ts {
2331823314
checkSourceElement(node.typeParameter);
2331923315
checkSourceElement(node.type);
2332023316

23321-
if (noImplicitAny && !node.type) {
23322-
reportImplicitAnyError(node, anyType);
23317+
if (!node.type) {
23318+
reportImplicitAny(node, anyType);
2332323319
}
2332423320

2332523321
const type = <MappedType>getTypeFromMappedTypeNode(node);
@@ -24343,8 +24339,8 @@ namespace ts {
2434324339
if (produceDiagnostics && !getEffectiveReturnTypeNode(node)) {
2434424340
// Report an implicit any error if there is no body, no explicit return type, and node is not a private method
2434524341
// in an ambient context
24346-
if (noImplicitAny && nodeIsMissing(body) && !isPrivateWithinAmbient(node)) {
24347-
reportImplicitAnyError(node, anyType);
24342+
if (nodeIsMissing(body) && !isPrivateWithinAmbient(node)) {
24343+
reportImplicitAny(node, anyType);
2434824344
}
2434924345

2435024346
if (functionFlags & FunctionFlags.Generator && nodeIsPresent(body)) {

Diff for: src/testRunner/unittests/tsserverProjectSystem.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4981,7 +4981,7 @@ namespace ts.projectSystem {
49814981
checkErrorMessage(session, "suggestionDiag", {
49824982
file: file.path,
49834983
diagnostics: [
4984-
createDiagnostic({ line: 1, offset: 12 }, { line: 1, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["p"], "suggestion", /*reportsUnnecssary*/ true)
4984+
createDiagnostic({ line: 1, offset: 12 }, { line: 1, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["p"], "suggestion", /*reportsUnnecessary*/ true),
49854985
],
49864986
});
49874987
checkCompleteEvent(session, 2, expectedSequenceId);

Diff for: tests/cases/fourslash/annotateWithTypeFromJSDoc1.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
/////** @type {number} */
55
////var [|x|];
66

7-
verify.getSuggestionDiagnostics([{
8-
message: "JSDoc types may be moved to TypeScript types.",
9-
code: 80004,
10-
}]);
7+
verify.getSuggestionDiagnostics([
8+
{ message: "JSDoc types may be moved to TypeScript types.", code: 80004 },
9+
{ message: "Variable 'x' implicitly has an 'any' type.", code: 7005 }]);
1110

1211
verify.codeFix({
1312
description: "Annotate with type from JSDoc",

Diff for: tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66
//// * @param alpha - the other best parameter
77
//// * @param {*} beta - I have no idea how this got here
88
//// */
9-
////function [|f|](x, y, z: string, alpha, beta) {
9+
////function [|f|]([|x|], [|y|], z: string, [|alpha|], [|beta|]) {
1010
//// x; y; z; alpha; beta;
1111
////}
1212

13-
verify.getSuggestionDiagnostics([{
14-
message: "JSDoc types may be moved to TypeScript types.",
15-
code: 80004,
16-
}]);
13+
const [r0, r1, r2, r3, r4] = test.ranges();
14+
verify.getSuggestionDiagnostics([
15+
{message: "JSDoc types may be moved to TypeScript types.", code: 80004, range: r0},
16+
{message: "Parameter 'x' implicitly has an 'any' type.", code: 7006, range: r1 },
17+
{message: "Parameter 'y' implicitly has an 'any' type.", code: 7006, range: r2 },
18+
{message: "Parameter 'alpha' implicitly has an 'any' type.", code: 7006, range: r3 },
19+
{message: "Parameter 'beta' implicitly has an 'any' type.", code: 7006, range: r4 }]);
1720

1821
verify.codeFix({
1922
description: "Annotate with type from JSDoc",

Diff for: tests/cases/fourslash/codeFixUnusedIdentifier_suggestion.ts

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66

77
const [r0, r1] = test.ranges();
88
verify.getSuggestionDiagnostics([
9+
{
10+
message: "Parameter 'p' implicitly has an 'any' type.",
11+
range: r0,
12+
code: 7006,
13+
},
914
{
1015
message: "'p' is declared but its value is never read.",
1116
range: r0,

Diff for: tests/cases/fourslash/noSuggestionDiagnosticsOnParseError.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,13 @@
44
////export {};
55
////const a = 1 d;
66

7-
verify.getSuggestionDiagnostics([]);
7+
// Only give suggestions for nodes that do NOT have parse errors
8+
verify.getSuggestionDiagnostics([{
9+
message: "Variable 'd' implicitly has an 'any' type.",
10+
code: 7005,
11+
range: {
12+
fileName: "/a.ts",
13+
pos: 23,
14+
end: 24,
15+
}
16+
}]);

Diff for: tests/cases/fourslash/refactorConvertToEs6Module_export_named.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
////exports.a3 = x => { x; };
1313
////exports.a4 = x => x;
1414

15-
verify.getSuggestionDiagnostics([{
16-
message: "File is a CommonJS module; it may be converted to an ES6 module.",
17-
code: 80001,
18-
}]);
15+
const [r0, r1, r2] = test.ranges();
16+
verify.getSuggestionDiagnostics([
17+
{ message: "File is a CommonJS module; it may be converted to an ES6 module.", code: 80001, range: r0 },
18+
]);
1919

2020
verify.codeFix({
2121
description: "Convert to ES6 module",

Diff for: tests/cases/fourslash/unusedLocalsInFunction2.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
//// x+1;
77
////}
88

9-
verify.rangeAfterCodeFix("var x;");
9+
verify.rangeAfterCodeFix("var x;", undefined, undefined, 0);

0 commit comments

Comments
 (0)