diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index 0c711cfa4c..520f8520d2 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -899,59 +899,42 @@ namespace ts.codefix { if (usage.isNumberOrString) { types.push(checker.getUnionType([checker.getStringType(), checker.getNumberType()])); } + if (usage.numberIndex) { + types.push(checker.createArrayType(unifyFromUsage(usage.numberIndex))); + } + if (usage.properties && usage.properties.size + || usage.calls && usage.calls.length + || usage.constructs && usage.constructs.length + || usage.stringIndex) { + types.push(inferStructuralType(usage)); + } types.push(...(usage.candidateTypes || []).map(t => checker.getBaseTypeOfLiteralType(t))); types.push(...inferNamedTypesFromProperties(usage)); - if (usage.numberIndex) { - types.push(checker.createArrayType(unifyFromUsage(usage.numberIndex))); - } - const structural = inferStructuralType(usage); - if (structural) { - types.push(structural); - } return types; } function inferStructuralType(usage: Usage) { - if (usage.properties && usage.properties.size - || usage.calls && usage.calls.length - || usage.constructs && usage.constructs.length - || usage.stringIndex) { - const members = createUnderscoreEscapedMap(); - const callSignatures: Signature[] = []; - const constructSignatures: Signature[] = []; - let stringIndexInfo: IndexInfo | undefined; - - if (usage.properties) { - usage.properties.forEach((u, name) => { - const symbol = checker.createSymbol(SymbolFlags.Property, name); - symbol.type = unifyFromUsage(u); - members.set(name, symbol); - }); - } - - if (usage.calls) { - callSignatures.push(getSignatureFromCalls(usage.calls)); - } - - if (usage.constructs) { - constructSignatures.push(getSignatureFromCalls(usage.constructs)); - } - - if (usage.stringIndex) { - stringIndexInfo = checker.createIndexInfo(unifyFromUsage(usage.stringIndex), /*isReadonly*/ false); - } - - return checker.createAnonymousType(/*symbol*/ undefined!, members, callSignatures, constructSignatures, stringIndexInfo, /*numberIndexInfo*/ undefined); // TODO: GH#18217 + const members = createUnderscoreEscapedMap(); + if (usage.properties) { + usage.properties.forEach((u, name) => { + const symbol = checker.createSymbol(SymbolFlags.Property, name); + symbol.type = unifyFromUsage(u); + members.set(name, symbol); + }); } + const callSignatures: Signature[] = usage.calls ? [getSignatureFromCalls(usage.calls)] : []; + const constructSignatures: Signature[] = usage.constructs ? [getSignatureFromCalls(usage.constructs)] : []; + const stringIndexInfo = usage.stringIndex && checker.createIndexInfo(unifyFromUsage(usage.stringIndex), /*isReadonly*/ false); + return checker.createAnonymousType(/*symbol*/ undefined!, members, callSignatures, constructSignatures, stringIndexInfo, /*numberIndexInfo*/ undefined); // TODO: GH#18217 } function inferNamedTypesFromProperties(usage: Usage): Type[] { if (!usage.properties || !usage.properties.size) return []; - const matches = builtins.filter(t => allPropertiesAreAssignableToUsage(t, usage)); - if (0 < matches.length && matches.length < 3) { - return matches.map(m => inferInstantiationFromUsage(m, usage)); + const types = builtins.filter(t => allPropertiesAreAssignableToUsage(t, usage)); + if (0 < types.length && types.length < 3) { + return types.map(t => inferInstantiationFromUsage(t, usage)); } return []; } diff --git a/tests/cases/fourslash/codeFixInferFromUsageAddition.ts b/tests/cases/fourslash/codeFixInferFromUsageAddition.ts new file mode 100644 index 0000000000..4b57d0603f --- /dev/null +++ b/tests/cases/fourslash/codeFixInferFromUsageAddition.ts @@ -0,0 +1,9 @@ +/// + +// @noImplicitAny: true +//// function foo([|a, m |]) { +//// return a + m +//// } + +verify.rangeAfterCodeFix("a: any, m: any", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0); + diff --git a/tests/cases/fourslash/codeFixInferFromUsageArray.ts b/tests/cases/fourslash/codeFixInferFromUsageArray.ts new file mode 100644 index 0000000000..03d6b09e99 --- /dev/null +++ b/tests/cases/fourslash/codeFixInferFromUsageArray.ts @@ -0,0 +1,14 @@ +/// + +// @noImplicitAny: true +//// function foo([|p, a, b, c, d, e |]) { +//// var x: string = a.pop() +//// b.reverse() +//// var rr: boolean[] = c.reverse() +//// d.some(t => t > 1); // can't infer from callbacks right now +//// var y = e.concat(12); // can't infer from overloaded functions right now +//// return p.push(12) +//// } + +verify.rangeAfterCodeFix("p: number[], a: string[], b: any[], c: boolean[], d: any[], e: number[]", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0); + diff --git a/tests/cases/fourslash/codeFixInferFromUsageLiteralTypes.ts b/tests/cases/fourslash/codeFixInferFromUsageLiteralTypes.ts new file mode 100644 index 0000000000..3b8c944115 --- /dev/null +++ b/tests/cases/fourslash/codeFixInferFromUsageLiteralTypes.ts @@ -0,0 +1,10 @@ +/// + +// @noImplicitAny: true +//// function foo([|a, m |]) { +//// a = 'hi' +//// m = 1 +//// } + +verify.rangeAfterCodeFix("a: string, m: number", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0); + diff --git a/tests/cases/fourslash/codeFixInferFromUsagePromise.ts b/tests/cases/fourslash/codeFixInferFromUsagePromise.ts new file mode 100644 index 0000000000..d06bb3b6a0 --- /dev/null +++ b/tests/cases/fourslash/codeFixInferFromUsagePromise.ts @@ -0,0 +1,9 @@ +/// + +// @noImplicitAny: true +//// function foo([|p |]) { +//// return p.then((x: string[]) => x[0]) +//// } + +verify.rangeAfterCodeFix("p: Promise", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0); + diff --git a/tests/cases/fourslash/codeFixInferFromUsageString.ts b/tests/cases/fourslash/codeFixInferFromUsageString.ts new file mode 100644 index 0000000000..dc5787dc75 --- /dev/null +++ b/tests/cases/fourslash/codeFixInferFromUsageString.ts @@ -0,0 +1,12 @@ +/// + +// @noImplicitAny: true +//// function foo([|p, a, b, c, d |]) { +//// var x +//// p.charAt(x) +//// a.charAt(0) +//// b.concat('hi') +//// } + +verify.rangeAfterCodeFix("p: string, a: string, b: string | string[]", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0); +