From 65cea207dad1eb593fcc47454e29cc0735949687 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 3 Mar 2017 14:32:18 -0800 Subject: [PATCH 1/4] Use getTypeOfExpression when inferring variable type from initializer --- src/compiler/checker.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c189475fd4dbb..a4c0acc405d5e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16131,7 +16131,7 @@ namespace ts { } function checkDeclarationInitializer(declaration: VariableLikeDeclaration) { - const type = checkExpressionCached(declaration.initializer); + const type = getTypeOfExpression(declaration.initializer, /*cache*/ true); return getCombinedNodeFlags(declaration) & NodeFlags.Const || getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration) || isTypeAssertion(declaration.initializer) ? type : getWidenedLiteralType(type); @@ -16204,10 +16204,12 @@ namespace ts { // Returns the type of an expression. Unlike checkExpression, this function is simply concerned // with computing the type and may not fully check all contained sub-expressions for errors. - function getTypeOfExpression(node: Expression) { + // A cache argument of true indicates that if the function performs a full type check, it is ok + // to cache the result. + function getTypeOfExpression(node: Expression, cache?: boolean) { // Optimize for the common case of a call to a function with a single non-generic call // signature where we can just fetch the return type without checking the arguments. - if (node.kind === SyntaxKind.CallExpression && (node).expression.kind !== SyntaxKind.SuperKeyword) { + if (node.kind === SyntaxKind.CallExpression && (node).expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(node, /*checkArgumentIsStringLiteral*/true)) { const funcType = checkNonNullExpression((node).expression); const signature = getSingleCallSignature(funcType); if (signature && !signature.typeParameters) { @@ -16217,7 +16219,7 @@ namespace ts { // Otherwise simply call checkExpression. Ideally, the entire family of checkXXX functions // should have a parameter that indicates whether full error checking is required such that // we can perform the optimizations locally. - return checkExpression(node); + return cache ? checkExpressionCached(node) : checkExpression(node); } // Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When From 6995055c861c2983acc9901da8c9b07779e27b92 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 3 Mar 2017 14:32:59 -0800 Subject: [PATCH 2/4] Accept new baselines --- .../reference/ambientRequireFunction.types | 2 +- .../controlFlowIterationErrors.errors.txt | 18 +----------------- ...implicitAnyFromCircularInference.errors.txt | 5 +---- 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/tests/baselines/reference/ambientRequireFunction.types b/tests/baselines/reference/ambientRequireFunction.types index e0341d97ec3e4..7b01a59268fc3 100644 --- a/tests/baselines/reference/ambientRequireFunction.types +++ b/tests/baselines/reference/ambientRequireFunction.types @@ -3,7 +3,7 @@ const fs = require("fs"); >fs : typeof "fs" ->require("fs") : any +>require("fs") : typeof "fs" >require : (moduleName: string) => any >"fs" : "fs" diff --git a/tests/baselines/reference/controlFlowIterationErrors.errors.txt b/tests/baselines/reference/controlFlowIterationErrors.errors.txt index 1f090ccd35fbb..bc4b19b0602bd 100644 --- a/tests/baselines/reference/controlFlowIterationErrors.errors.txt +++ b/tests/baselines/reference/controlFlowIterationErrors.errors.txt @@ -6,15 +6,9 @@ tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(35,17): error Type 'string' is not assignable to type 'number'. tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(46,17): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'. Type 'string' is not assignable to type 'number'. -tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(77,13): error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. -tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(77,26): error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'. - Type 'true' is not assignable to type 'string | number'. -tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(88,13): error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. -tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(88,26): error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'. - Type 'true' is not assignable to type 'string | number'. -==== tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts (8 errors) ==== +==== tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts (4 errors) ==== let cond: boolean; @@ -104,11 +98,6 @@ tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(88,26): error x = "0"; while (cond) { let y = asNumber(x); - ~ -!!! error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. - ~ -!!! error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'. -!!! error TS2345: Type 'true' is not assignable to type 'string | number'. x = y + 1; x; } @@ -120,11 +109,6 @@ tests/cases/conformance/controlFlow/controlFlowIterationErrors.ts(88,26): error while (cond) { x; let y = asNumber(x); - ~ -!!! error TS7022: 'y' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. - ~ -!!! error TS2345: Argument of type 'string | number | boolean' is not assignable to parameter of type 'string | number'. -!!! error TS2345: Type 'true' is not assignable to type 'string | number'. x = y + 1; x; } diff --git a/tests/baselines/reference/implicitAnyFromCircularInference.errors.txt b/tests/baselines/reference/implicitAnyFromCircularInference.errors.txt index bb43d96b003aa..7d4dfa2cf5e39 100644 --- a/tests/baselines/reference/implicitAnyFromCircularInference.errors.txt +++ b/tests/baselines/reference/implicitAnyFromCircularInference.errors.txt @@ -7,11 +7,10 @@ tests/cases/compiler/implicitAnyFromCircularInference.ts(18,10): error TS7024: F tests/cases/compiler/implicitAnyFromCircularInference.ts(23,10): error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. tests/cases/compiler/implicitAnyFromCircularInference.ts(26,10): error TS7023: 'h' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. tests/cases/compiler/implicitAnyFromCircularInference.ts(28,14): error TS7023: 'foo' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. -tests/cases/compiler/implicitAnyFromCircularInference.ts(41,5): error TS7022: 's' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. tests/cases/compiler/implicitAnyFromCircularInference.ts(46,9): error TS7023: 'x' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. -==== tests/cases/compiler/implicitAnyFromCircularInference.ts (11 errors) ==== +==== tests/cases/compiler/implicitAnyFromCircularInference.ts (10 errors) ==== // Error expected var a: typeof a; @@ -71,8 +70,6 @@ tests/cases/compiler/implicitAnyFromCircularInference.ts(46,9): error TS7023: 'x class C { // Error expected s = foo(this); - ~~~~~~~~~~~~~~ -!!! error TS7022: 's' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. } class D { From c2431ade0cf271d2cbec7b651f06030493ec23a8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 3 Mar 2017 14:33:07 -0800 Subject: [PATCH 3/4] Add regression test --- .../circularInferredTypeOfVariable.js | 44 +++++++++++++++++ .../circularInferredTypeOfVariable.symbols | 34 ++++++++++++++ .../circularInferredTypeOfVariable.types | 47 +++++++++++++++++++ .../circularInferredTypeOfVariable.ts | 20 ++++++++ 4 files changed, 145 insertions(+) create mode 100644 tests/baselines/reference/circularInferredTypeOfVariable.js create mode 100644 tests/baselines/reference/circularInferredTypeOfVariable.symbols create mode 100644 tests/baselines/reference/circularInferredTypeOfVariable.types create mode 100644 tests/cases/compiler/circularInferredTypeOfVariable.ts diff --git a/tests/baselines/reference/circularInferredTypeOfVariable.js b/tests/baselines/reference/circularInferredTypeOfVariable.js new file mode 100644 index 0000000000000..38c5334761a9b --- /dev/null +++ b/tests/baselines/reference/circularInferredTypeOfVariable.js @@ -0,0 +1,44 @@ +//// [circularInferredTypeOfVariable.ts] + +// Repro from #14428 + +(async () => { + function foo(p: string[]): string[] { + return []; + } + + function bar(p: string[]): string[] { + return []; + } + + let a1: string[] | undefined = []; + + while (true) { + let a2 = foo(a1!); + a1 = await bar(a2); + } +}); + +//// [circularInferredTypeOfVariable.js] +// Repro from #14428 +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +(() => __awaiter(this, void 0, void 0, function* () { + function foo(p) { + return []; + } + function bar(p) { + return []; + } + let a1 = []; + while (true) { + let a2 = foo(a1); + a1 = yield bar(a2); + } +})); diff --git a/tests/baselines/reference/circularInferredTypeOfVariable.symbols b/tests/baselines/reference/circularInferredTypeOfVariable.symbols new file mode 100644 index 0000000000000..653978f0e83b5 --- /dev/null +++ b/tests/baselines/reference/circularInferredTypeOfVariable.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/circularInferredTypeOfVariable.ts === + +// Repro from #14428 + +(async () => { + function foo(p: string[]): string[] { +>foo : Symbol(foo, Decl(circularInferredTypeOfVariable.ts, 3, 14)) +>p : Symbol(p, Decl(circularInferredTypeOfVariable.ts, 4, 17)) + + return []; + } + + function bar(p: string[]): string[] { +>bar : Symbol(bar, Decl(circularInferredTypeOfVariable.ts, 6, 5)) +>p : Symbol(p, Decl(circularInferredTypeOfVariable.ts, 8, 17)) + + return []; + } + + let a1: string[] | undefined = []; +>a1 : Symbol(a1, Decl(circularInferredTypeOfVariable.ts, 12, 7)) + + while (true) { + let a2 = foo(a1!); +>a2 : Symbol(a2, Decl(circularInferredTypeOfVariable.ts, 15, 11)) +>foo : Symbol(foo, Decl(circularInferredTypeOfVariable.ts, 3, 14)) +>a1 : Symbol(a1, Decl(circularInferredTypeOfVariable.ts, 12, 7)) + + a1 = await bar(a2); +>a1 : Symbol(a1, Decl(circularInferredTypeOfVariable.ts, 12, 7)) +>bar : Symbol(bar, Decl(circularInferredTypeOfVariable.ts, 6, 5)) +>a2 : Symbol(a2, Decl(circularInferredTypeOfVariable.ts, 15, 11)) + } +}); diff --git a/tests/baselines/reference/circularInferredTypeOfVariable.types b/tests/baselines/reference/circularInferredTypeOfVariable.types new file mode 100644 index 0000000000000..df227bd9ae001 --- /dev/null +++ b/tests/baselines/reference/circularInferredTypeOfVariable.types @@ -0,0 +1,47 @@ +=== tests/cases/compiler/circularInferredTypeOfVariable.ts === + +// Repro from #14428 + +(async () => { +>(async () => { function foo(p: string[]): string[] { return []; } function bar(p: string[]): string[] { return []; } let a1: string[] | undefined = []; while (true) { let a2 = foo(a1!); a1 = await bar(a2); }}) : () => Promise +>async () => { function foo(p: string[]): string[] { return []; } function bar(p: string[]): string[] { return []; } let a1: string[] | undefined = []; while (true) { let a2 = foo(a1!); a1 = await bar(a2); }} : () => Promise + + function foo(p: string[]): string[] { +>foo : (p: string[]) => string[] +>p : string[] + + return []; +>[] : undefined[] + } + + function bar(p: string[]): string[] { +>bar : (p: string[]) => string[] +>p : string[] + + return []; +>[] : undefined[] + } + + let a1: string[] | undefined = []; +>a1 : string[] +>[] : undefined[] + + while (true) { +>true : true + + let a2 = foo(a1!); +>a2 : string[] +>foo(a1!) : string[] +>foo : (p: string[]) => string[] +>a1! : string[] +>a1 : string[] + + a1 = await bar(a2); +>a1 = await bar(a2) : string[] +>a1 : string[] +>await bar(a2) : string[] +>bar(a2) : string[] +>bar : (p: string[]) => string[] +>a2 : string[] + } +}); diff --git a/tests/cases/compiler/circularInferredTypeOfVariable.ts b/tests/cases/compiler/circularInferredTypeOfVariable.ts new file mode 100644 index 0000000000000..662b6bfc8d8ba --- /dev/null +++ b/tests/cases/compiler/circularInferredTypeOfVariable.ts @@ -0,0 +1,20 @@ +// @target: es6 + +// Repro from #14428 + +(async () => { + function foo(p: string[]): string[] { + return []; + } + + function bar(p: string[]): string[] { + return []; + } + + let a1: string[] | undefined = []; + + while (true) { + let a2 = foo(a1!); + a1 = await bar(a2); + } +}); \ No newline at end of file From 56e2735f5677811d5b519e18b0a48d352c79f647 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 3 Mar 2017 14:57:14 -0800 Subject: [PATCH 4/4] Fix fourslash test --- tests/cases/fourslash/extendArrayInterfaceMember.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cases/fourslash/extendArrayInterfaceMember.ts b/tests/cases/fourslash/extendArrayInterfaceMember.ts index ef3c06b205375..22aeed01f1d49 100644 --- a/tests/cases/fourslash/extendArrayInterfaceMember.ts +++ b/tests/cases/fourslash/extendArrayInterfaceMember.ts @@ -10,7 +10,7 @@ verify.numberOfErrorsInCurrentFile(1); // - Supplied parameters do not match any signature of call target. // - Could not select overload for 'call' expression. -verify.quickInfoAt("y", "var y: any"); +verify.quickInfoAt("y", "var y: number"); goTo.eof(); edit.insert("interface Array { pop(def: T): T; }");