Prefer a likely literal over anonymous type in --noImplicitAny codefixes (#36015)

* Prefer a likely literal over anonymous type in --noImplicitAny codefixes

Before trying to make an anonymous type for a type's usage, we'll first check if there is exactly one builtin primitive the usage is assignable to, and use it if so. Right now that's only `number` and `string` because `boolean` has no distinguishable members.

A couple of implementation details:
* `tryInsertTypeAnnotation` needed to know to insert a type _after_ a node's `exclamationToken` if it exists
* This code area was written before `??` 😉

* Used unknown/any instead of void when applicable

* Fix little whitespace change in tests/cases/fourslash/codeFixInferFromUsagePropertyAccessJS.ts

* Undid some now-unnecessary unknown additions

* Took advice on restricting void to just call expressions
This commit is contained in:
Josh Goldberg 2020-04-22 14:28:11 -04:00 committed by GitHub
parent f248567dab
commit ef83109dbf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 28 additions and 8 deletions

View file

@ -603,7 +603,7 @@ namespace ts.codefix {
switch (node.parent.kind) {
case SyntaxKind.ExpressionStatement:
addCandidateType(usage, checker.getVoidType());
inferTypeFromExpressionStatement(node, usage);
break;
case SyntaxKind.PostfixUnaryExpression:
usage.isNumber = true;
@ -661,6 +661,10 @@ namespace ts.codefix {
}
}
function inferTypeFromExpressionStatement(node: Expression, usage: Usage): void {
addCandidateType(usage, isCallExpression(node) ? checker.getVoidType() : checker.getAnyType());
}
function inferTypeFromPrefixUnaryExpression(node: PrefixUnaryExpression, usage: Usage): void {
switch (node.operator) {
case SyntaxKind.PlusPlusToken:
@ -960,10 +964,7 @@ namespace ts.codefix {
if (usage.numberIndex) {
types.push(checker.createArrayType(combineFromUsage(usage.numberIndex)));
}
if (usage.properties && usage.properties.size
|| usage.calls && usage.calls.length
|| usage.constructs && usage.constructs.length
|| usage.stringIndex) {
if (usage.properties?.size || usage.calls?.length || usage.constructs?.length || usage.stringIndex) {
types.push(inferStructuralType(usage));
}

View file

@ -434,7 +434,7 @@ namespace ts.textChanges {
}
}
else {
endNode = node.kind !== SyntaxKind.VariableDeclaration && node.questionToken ? node.questionToken : node.name;
endNode = (node.kind === SyntaxKind.VariableDeclaration ? node.exclamationToken : node.questionToken) ?? node.name;
}
this.insertNodeAt(sourceFile, endNode.end, type, { prefix: ": " });

View file

@ -0,0 +1,10 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// function foo([|text |]) {
//// text.length;
//// text.indexOf("z");
//// text.charAt(0);
//// }
verify.rangeAfterCodeFix("text: string", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0);

View file

@ -12,4 +12,4 @@
//// return x.y.z
////}
verify.rangeAfterCodeFix("a: { b: { c: void; }; }, m: { n: () => number; }, x: { y: { z: number[]; }; }", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0);
verify.rangeAfterCodeFix("a: { b: { c: any; }; }, m: { n: () => number; }, x: { y: { z: number[]; }; }", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0);

View file

@ -21,7 +21,7 @@ verify.codeFix({
index: 0,
newFileContent:
`/**
* @param {{ b: { c: void; }; }} a
* @param {{ b: { c: any; }; }} a
* @param {{ n: () => number; }} m
* @param {{ y: { z: number[]; }; }} x
*/

View file

@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
//// let [|text! |];
//// text.length;
//// text.indexOf("z");
//// text.charAt(0);
verify.rangeAfterCodeFix("text!: string", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0);