From a0dc55412d7430365bcf954c42f3d1c60567dce3 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 26 Mar 2018 16:14:12 -0700
Subject: [PATCH 1/4] Bind toplevel this assignments as global decls
---
src/compiler/binder.ts | 5 +++++
.../reference/topLevelThisAssignment.js | 10 ++++++++++
.../reference/topLevelThisAssignment.symbols | 8 ++++++++
.../reference/topLevelThisAssignment.types | 16 ++++++++++++++++
.../conformance/salsa/topLevelThisAssignment.ts | 6 ++++++
5 files changed, 45 insertions(+)
create mode 100644 tests/baselines/reference/topLevelThisAssignment.js
create mode 100644 tests/baselines/reference/topLevelThisAssignment.symbols
create mode 100644 tests/baselines/reference/topLevelThisAssignment.types
create mode 100644 tests/cases/conformance/salsa/topLevelThisAssignment.ts
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 2b66e92cdc357..4062dc9fbd8d0 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -2362,6 +2362,11 @@ namespace ts {
const symbolTable = hasModifier(thisContainer, ModifierFlags.Static) ? containingClass.symbol.exports : containingClass.symbol.members;
declareSymbol(symbolTable, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None, /*isReplaceableByMethod*/ true);
break;
+ case SyntaxKind.SourceFile:
+ // this.foo assignment in a source file
+ // Bind this property in the global namespace
+ declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
+ break;
default:
Debug.fail(Debug.showSyntaxKind(thisContainer));
diff --git a/tests/baselines/reference/topLevelThisAssignment.js b/tests/baselines/reference/topLevelThisAssignment.js
new file mode 100644
index 0000000000000..d8735fc42c9a1
--- /dev/null
+++ b/tests/baselines/reference/topLevelThisAssignment.js
@@ -0,0 +1,10 @@
+//// [a.js]
+this.a = 10;
+this.a;
+a;
+
+
+//// [output.js]
+this.a = 10;
+this.a;
+a;
diff --git a/tests/baselines/reference/topLevelThisAssignment.symbols b/tests/baselines/reference/topLevelThisAssignment.symbols
new file mode 100644
index 0000000000000..e01f9dfd0bd94
--- /dev/null
+++ b/tests/baselines/reference/topLevelThisAssignment.symbols
@@ -0,0 +1,8 @@
+=== tests/cases/conformance/salsa/a.js ===
+this.a = 10;
+>a : Symbol(a, Decl(a.js, 0, 0))
+
+this.a;
+a;
+>a : Symbol(a, Decl(a.js, 0, 0))
+
diff --git a/tests/baselines/reference/topLevelThisAssignment.types b/tests/baselines/reference/topLevelThisAssignment.types
new file mode 100644
index 0000000000000..e6c2843354645
--- /dev/null
+++ b/tests/baselines/reference/topLevelThisAssignment.types
@@ -0,0 +1,16 @@
+=== tests/cases/conformance/salsa/a.js ===
+this.a = 10;
+>this.a = 10 : 10
+>this.a : any
+>this : any
+>a : any
+>10 : 10
+
+this.a;
+>this.a : any
+>this : any
+>a : any
+
+a;
+>a : number
+
diff --git a/tests/cases/conformance/salsa/topLevelThisAssignment.ts b/tests/cases/conformance/salsa/topLevelThisAssignment.ts
new file mode 100644
index 0000000000000..0f01e425f052f
--- /dev/null
+++ b/tests/cases/conformance/salsa/topLevelThisAssignment.ts
@@ -0,0 +1,6 @@
+// @out: output.js
+// @allowJs: true
+// @Filename: a.js
+this.a = 10;
+this.a;
+a;
From e58da912acd7188ed7968589fd260e9fa0faff03 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 27 Mar 2018 11:01:05 -0700
Subject: [PATCH 2/4] Improve toplevel this-assignment test
Make sure that the global declaration is visible in other files
---
tests/baselines/reference/topLevelThisAssignment.js | 8 ++++++++
tests/baselines/reference/topLevelThisAssignment.symbols | 5 +++++
tests/baselines/reference/topLevelThisAssignment.types | 9 +++++++++
tests/cases/conformance/salsa/topLevelThisAssignment.ts | 4 ++++
4 files changed, 26 insertions(+)
diff --git a/tests/baselines/reference/topLevelThisAssignment.js b/tests/baselines/reference/topLevelThisAssignment.js
index d8735fc42c9a1..c87fad289f709 100644
--- a/tests/baselines/reference/topLevelThisAssignment.js
+++ b/tests/baselines/reference/topLevelThisAssignment.js
@@ -1,10 +1,18 @@
+//// [tests/cases/conformance/salsa/topLevelThisAssignment.ts] ////
+
//// [a.js]
this.a = 10;
this.a;
a;
+//// [b.js]
+this.a;
+a;
+
//// [output.js]
this.a = 10;
this.a;
a;
+this.a;
+a;
diff --git a/tests/baselines/reference/topLevelThisAssignment.symbols b/tests/baselines/reference/topLevelThisAssignment.symbols
index e01f9dfd0bd94..6f1d85ed77059 100644
--- a/tests/baselines/reference/topLevelThisAssignment.symbols
+++ b/tests/baselines/reference/topLevelThisAssignment.symbols
@@ -6,3 +6,8 @@ this.a;
a;
>a : Symbol(a, Decl(a.js, 0, 0))
+=== tests/cases/conformance/salsa/b.js ===
+this.a;
+a;
+>a : Symbol(a, Decl(a.js, 0, 0))
+
diff --git a/tests/baselines/reference/topLevelThisAssignment.types b/tests/baselines/reference/topLevelThisAssignment.types
index e6c2843354645..5d4fe35a6b6f4 100644
--- a/tests/baselines/reference/topLevelThisAssignment.types
+++ b/tests/baselines/reference/topLevelThisAssignment.types
@@ -14,3 +14,12 @@ this.a;
a;
>a : number
+=== tests/cases/conformance/salsa/b.js ===
+this.a;
+>this.a : any
+>this : any
+>a : any
+
+a;
+>a : number
+
diff --git a/tests/cases/conformance/salsa/topLevelThisAssignment.ts b/tests/cases/conformance/salsa/topLevelThisAssignment.ts
index 0f01e425f052f..162bed0c30fc7 100644
--- a/tests/cases/conformance/salsa/topLevelThisAssignment.ts
+++ b/tests/cases/conformance/salsa/topLevelThisAssignment.ts
@@ -4,3 +4,7 @@
this.a = 10;
this.a;
a;
+
+// @Filename: b.js
+this.a;
+a;
From 5831e807a7262a569295cd75168db2017fd7585a Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 27 Mar 2018 15:43:21 -0700
Subject: [PATCH 3/4] Bind+check toplevel this in commonjs
---
src/compiler/binder.ts | 9 +++-
src/compiler/checker.ts | 5 ++
.../topLevelThisAssignment.errors.txt | 24 +++++++++
.../reference/topLevelThisAssignment.js | 13 +++++
.../reference/topLevelThisAssignment.symbols | 18 +++++++
.../reference/topLevelThisAssignment.types | 42 +++++++++++----
.../topLevelThisAssignment2.errors.txt | 20 +++++++
.../reference/topLevelThisAssignment2.symbols | 44 ++++++++++++++++
.../reference/topLevelThisAssignment2.types | 52 +++++++++++++++++++
.../typeFromPropertyAssignment9.symbols | 4 ++
.../typeFromPropertyAssignment9.types | 10 ++--
.../salsa/topLevelThisAssignment.ts | 8 +++
.../salsa/topLevelThisAssignment2.ts | 17 ++++++
13 files changed, 250 insertions(+), 16 deletions(-)
create mode 100644 tests/baselines/reference/topLevelThisAssignment.errors.txt
create mode 100644 tests/baselines/reference/topLevelThisAssignment2.errors.txt
create mode 100644 tests/baselines/reference/topLevelThisAssignment2.symbols
create mode 100644 tests/baselines/reference/topLevelThisAssignment2.types
create mode 100644 tests/cases/conformance/salsa/topLevelThisAssignment2.ts
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 4062dc9fbd8d0..fe8910cf5ee00 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -2364,8 +2364,13 @@ namespace ts {
break;
case SyntaxKind.SourceFile:
// this.foo assignment in a source file
- // Bind this property in the global namespace
- declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
+ // Bind this property in the global namespace or in the exports if in commonjs
+ if ((thisContainer as SourceFile).commonJsModuleIndicator) {
+ declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None);
+ }
+ else {
+ declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
+ }
break;
default:
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 08c67097a9c8b..8c4d31c6ad219 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -13952,6 +13952,11 @@ namespace ts {
if (type && type !== unknownType) {
return getFlowTypeOfReference(node, type);
}
+ if (isSourceFile(container)) {
+ // look up in the source file's locals or exports
+ const parent = getSymbolOfNode(container);
+ return createAnonymousType(parent, container.commonJsModuleIndicator ? parent.exports : globals, emptyArray, emptyArray, createIndexInfo(anyType, /*isReadonly*/ false), undefined);
+ }
}
}
diff --git a/tests/baselines/reference/topLevelThisAssignment.errors.txt b/tests/baselines/reference/topLevelThisAssignment.errors.txt
new file mode 100644
index 0000000000000..a24f51f7fbd32
--- /dev/null
+++ b/tests/baselines/reference/topLevelThisAssignment.errors.txt
@@ -0,0 +1,24 @@
+tests/cases/conformance/salsa/a.js(4,1): error TS2304: Cannot find name 'unknown'.
+tests/cases/conformance/salsa/b.js(3,1): error TS2304: Cannot find name 'unknown'.
+
+
+==== tests/cases/conformance/salsa/a.js (1 errors) ====
+ this.a = 10;
+ this.a;
+ a;
+ unknown;
+ ~~~~~~~
+!!! error TS2304: Cannot find name 'unknown'.
+ this.unknown;
+
+ // also, improved types for this-prefixed globals like eval:
+ this.eval('hi');
+
+==== tests/cases/conformance/salsa/b.js (1 errors) ====
+ this.a;
+ a;
+ unknown;
+ ~~~~~~~
+!!! error TS2304: Cannot find name 'unknown'.
+ this.unknown;
+
\ No newline at end of file
diff --git a/tests/baselines/reference/topLevelThisAssignment.js b/tests/baselines/reference/topLevelThisAssignment.js
index c87fad289f709..55e5342e33361 100644
--- a/tests/baselines/reference/topLevelThisAssignment.js
+++ b/tests/baselines/reference/topLevelThisAssignment.js
@@ -4,15 +4,28 @@
this.a = 10;
this.a;
a;
+unknown;
+this.unknown;
+
+// also, improved types for this-prefixed globals like eval:
+this.eval('hi');
//// [b.js]
this.a;
a;
+unknown;
+this.unknown;
//// [output.js]
this.a = 10;
this.a;
a;
+unknown;
+this.unknown;
+// also, improved types for this-prefixed globals like eval:
+this.eval('hi');
this.a;
a;
+unknown;
+this.unknown;
diff --git a/tests/baselines/reference/topLevelThisAssignment.symbols b/tests/baselines/reference/topLevelThisAssignment.symbols
index 6f1d85ed77059..dd3ba31d2bf04 100644
--- a/tests/baselines/reference/topLevelThisAssignment.symbols
+++ b/tests/baselines/reference/topLevelThisAssignment.symbols
@@ -1,13 +1,31 @@
=== tests/cases/conformance/salsa/a.js ===
this.a = 10;
+>this.a : Symbol(a, Decl(a.js, 0, 0))
>a : Symbol(a, Decl(a.js, 0, 0))
this.a;
+>this.a : Symbol(a, Decl(a.js, 0, 0))
+>a : Symbol(a, Decl(a.js, 0, 0))
+
a;
>a : Symbol(a, Decl(a.js, 0, 0))
+unknown;
+this.unknown;
+
+// also, improved types for this-prefixed globals like eval:
+this.eval('hi');
+>this.eval : Symbol(eval, Decl(lib.d.ts, --, --))
+>eval : Symbol(eval, Decl(lib.d.ts, --, --))
+
=== tests/cases/conformance/salsa/b.js ===
this.a;
+>this.a : Symbol(a, Decl(a.js, 0, 0))
+>a : Symbol(a, Decl(a.js, 0, 0))
+
a;
>a : Symbol(a, Decl(a.js, 0, 0))
+unknown;
+this.unknown;
+
diff --git a/tests/baselines/reference/topLevelThisAssignment.types b/tests/baselines/reference/topLevelThisAssignment.types
index 5d4fe35a6b6f4..45cc6416fc8ed 100644
--- a/tests/baselines/reference/topLevelThisAssignment.types
+++ b/tests/baselines/reference/topLevelThisAssignment.types
@@ -1,25 +1,49 @@
=== tests/cases/conformance/salsa/a.js ===
this.a = 10;
>this.a = 10 : 10
->this.a : any
->this : any
->a : any
+>this.a : number
+>this : { [x: string]: any; eval(x: string): any; parseInt(s: string, radix?: number): number; parseFloat(string: string): number; isNaN(number: number): boolean; isFinite(number: number): boolean; decodeURI(encodedURI: string): string; decodeURIComponent(encodedURIComponent: string): string; encodeURI(uri: string): string; encodeURIComponent(uriComponent: string): string; escape(string: string): string; unescape(string: string): string; readonly NaN: number; readonly Infinity: number; readonly Object: ObjectConstructor; readonly Function: FunctionConstructor; readonly String: StringConstructor; readonly Boolean: BooleanConstructor; readonly Number: NumberConstructor; readonly Math: Math; readonly Date: DateConstructor; readonly RegExp: RegExpConstructor; readonly Error: ErrorConstructor; readonly EvalError: EvalErrorConstructor; readonly RangeError: RangeErrorConstructor; readonly ReferenceError: ReferenceErrorConstructor; readonly SyntaxError: SyntaxErrorConstructor; readonly TypeError: TypeErrorConstructor; readonly URIError: URIErrorConstructor; readonly JSON: JSON; readonly Array: ArrayConstructor; readonly ArrayBuffer: ArrayBufferConstructor; readonly DataView: DataViewConstructor; readonly Int8Array: Int8ArrayConstructor; readonly Uint8Array: Uint8ArrayConstructor; readonly Uint8ClampedArray: Uint8ClampedArrayConstructor; readonly Int16Array: Int16ArrayConstructor; readonly Uint16Array: Uint16ArrayConstructor; readonly Int32Array: Int32ArrayConstructor; readonly Uint32Array: Uint32ArrayConstructor; readonly Float32Array: Float32ArrayConstructor; readonly Float64Array: Float64ArrayConstructor; Intl: typeof Intl; a: number; undefined: undefined; }
+>a : number
>10 : 10
this.a;
->this.a : any
->this : any
->a : any
+>this.a : number
+>this : { [x: string]: any; eval(x: string): any; parseInt(s: string, radix?: number): number; parseFloat(string: string): number; isNaN(number: number): boolean; isFinite(number: number): boolean; decodeURI(encodedURI: string): string; decodeURIComponent(encodedURIComponent: string): string; encodeURI(uri: string): string; encodeURIComponent(uriComponent: string): string; escape(string: string): string; unescape(string: string): string; readonly NaN: number; readonly Infinity: number; readonly Object: ObjectConstructor; readonly Function: FunctionConstructor; readonly String: StringConstructor; readonly Boolean: BooleanConstructor; readonly Number: NumberConstructor; readonly Math: Math; readonly Date: DateConstructor; readonly RegExp: RegExpConstructor; readonly Error: ErrorConstructor; readonly EvalError: EvalErrorConstructor; readonly RangeError: RangeErrorConstructor; readonly ReferenceError: ReferenceErrorConstructor; readonly SyntaxError: SyntaxErrorConstructor; readonly TypeError: TypeErrorConstructor; readonly URIError: URIErrorConstructor; readonly JSON: JSON; readonly Array: ArrayConstructor; readonly ArrayBuffer: ArrayBufferConstructor; readonly DataView: DataViewConstructor; readonly Int8Array: Int8ArrayConstructor; readonly Uint8Array: Uint8ArrayConstructor; readonly Uint8ClampedArray: Uint8ClampedArrayConstructor; readonly Int16Array: Int16ArrayConstructor; readonly Uint16Array: Uint16ArrayConstructor; readonly Int32Array: Int32ArrayConstructor; readonly Uint32Array: Uint32ArrayConstructor; readonly Float32Array: Float32ArrayConstructor; readonly Float64Array: Float64ArrayConstructor; Intl: typeof Intl; a: number; undefined: undefined; }
+>a : number
a;
>a : number
+unknown;
+>unknown : any
+
+this.unknown;
+>this.unknown : any
+>this : { [x: string]: any; eval(x: string): any; parseInt(s: string, radix?: number): number; parseFloat(string: string): number; isNaN(number: number): boolean; isFinite(number: number): boolean; decodeURI(encodedURI: string): string; decodeURIComponent(encodedURIComponent: string): string; encodeURI(uri: string): string; encodeURIComponent(uriComponent: string): string; escape(string: string): string; unescape(string: string): string; readonly NaN: number; readonly Infinity: number; readonly Object: ObjectConstructor; readonly Function: FunctionConstructor; readonly String: StringConstructor; readonly Boolean: BooleanConstructor; readonly Number: NumberConstructor; readonly Math: Math; readonly Date: DateConstructor; readonly RegExp: RegExpConstructor; readonly Error: ErrorConstructor; readonly EvalError: EvalErrorConstructor; readonly RangeError: RangeErrorConstructor; readonly ReferenceError: ReferenceErrorConstructor; readonly SyntaxError: SyntaxErrorConstructor; readonly TypeError: TypeErrorConstructor; readonly URIError: URIErrorConstructor; readonly JSON: JSON; readonly Array: ArrayConstructor; readonly ArrayBuffer: ArrayBufferConstructor; readonly DataView: DataViewConstructor; readonly Int8Array: Int8ArrayConstructor; readonly Uint8Array: Uint8ArrayConstructor; readonly Uint8ClampedArray: Uint8ClampedArrayConstructor; readonly Int16Array: Int16ArrayConstructor; readonly Uint16Array: Uint16ArrayConstructor; readonly Int32Array: Int32ArrayConstructor; readonly Uint32Array: Uint32ArrayConstructor; readonly Float32Array: Float32ArrayConstructor; readonly Float64Array: Float64ArrayConstructor; Intl: typeof Intl; a: number; undefined: undefined; }
+>unknown : any
+
+// also, improved types for this-prefixed globals like eval:
+this.eval('hi');
+>this.eval('hi') : any
+>this.eval : (x: string) => any
+>this : { [x: string]: any; eval(x: string): any; parseInt(s: string, radix?: number): number; parseFloat(string: string): number; isNaN(number: number): boolean; isFinite(number: number): boolean; decodeURI(encodedURI: string): string; decodeURIComponent(encodedURIComponent: string): string; encodeURI(uri: string): string; encodeURIComponent(uriComponent: string): string; escape(string: string): string; unescape(string: string): string; readonly NaN: number; readonly Infinity: number; readonly Object: ObjectConstructor; readonly Function: FunctionConstructor; readonly String: StringConstructor; readonly Boolean: BooleanConstructor; readonly Number: NumberConstructor; readonly Math: Math; readonly Date: DateConstructor; readonly RegExp: RegExpConstructor; readonly Error: ErrorConstructor; readonly EvalError: EvalErrorConstructor; readonly RangeError: RangeErrorConstructor; readonly ReferenceError: ReferenceErrorConstructor; readonly SyntaxError: SyntaxErrorConstructor; readonly TypeError: TypeErrorConstructor; readonly URIError: URIErrorConstructor; readonly JSON: JSON; readonly Array: ArrayConstructor; readonly ArrayBuffer: ArrayBufferConstructor; readonly DataView: DataViewConstructor; readonly Int8Array: Int8ArrayConstructor; readonly Uint8Array: Uint8ArrayConstructor; readonly Uint8ClampedArray: Uint8ClampedArrayConstructor; readonly Int16Array: Int16ArrayConstructor; readonly Uint16Array: Uint16ArrayConstructor; readonly Int32Array: Int32ArrayConstructor; readonly Uint32Array: Uint32ArrayConstructor; readonly Float32Array: Float32ArrayConstructor; readonly Float64Array: Float64ArrayConstructor; Intl: typeof Intl; a: number; undefined: undefined; }
+>eval : (x: string) => any
+>'hi' : "hi"
+
=== tests/cases/conformance/salsa/b.js ===
this.a;
->this.a : any
->this : any
->a : any
+>this.a : number
+>this : { [x: string]: any; eval(x: string): any; parseInt(s: string, radix?: number): number; parseFloat(string: string): number; isNaN(number: number): boolean; isFinite(number: number): boolean; decodeURI(encodedURI: string): string; decodeURIComponent(encodedURIComponent: string): string; encodeURI(uri: string): string; encodeURIComponent(uriComponent: string): string; escape(string: string): string; unescape(string: string): string; readonly NaN: number; readonly Infinity: number; readonly Object: ObjectConstructor; readonly Function: FunctionConstructor; readonly String: StringConstructor; readonly Boolean: BooleanConstructor; readonly Number: NumberConstructor; readonly Math: Math; readonly Date: DateConstructor; readonly RegExp: RegExpConstructor; readonly Error: ErrorConstructor; readonly EvalError: EvalErrorConstructor; readonly RangeError: RangeErrorConstructor; readonly ReferenceError: ReferenceErrorConstructor; readonly SyntaxError: SyntaxErrorConstructor; readonly TypeError: TypeErrorConstructor; readonly URIError: URIErrorConstructor; readonly JSON: JSON; readonly Array: ArrayConstructor; readonly ArrayBuffer: ArrayBufferConstructor; readonly DataView: DataViewConstructor; readonly Int8Array: Int8ArrayConstructor; readonly Uint8Array: Uint8ArrayConstructor; readonly Uint8ClampedArray: Uint8ClampedArrayConstructor; readonly Int16Array: Int16ArrayConstructor; readonly Uint16Array: Uint16ArrayConstructor; readonly Int32Array: Int32ArrayConstructor; readonly Uint32Array: Uint32ArrayConstructor; readonly Float32Array: Float32ArrayConstructor; readonly Float64Array: Float64ArrayConstructor; Intl: typeof Intl; a: number; undefined: undefined; }
+>a : number
a;
>a : number
+unknown;
+>unknown : any
+
+this.unknown;
+>this.unknown : any
+>this : { [x: string]: any; eval(x: string): any; parseInt(s: string, radix?: number): number; parseFloat(string: string): number; isNaN(number: number): boolean; isFinite(number: number): boolean; decodeURI(encodedURI: string): string; decodeURIComponent(encodedURIComponent: string): string; encodeURI(uri: string): string; encodeURIComponent(uriComponent: string): string; escape(string: string): string; unescape(string: string): string; readonly NaN: number; readonly Infinity: number; readonly Object: ObjectConstructor; readonly Function: FunctionConstructor; readonly String: StringConstructor; readonly Boolean: BooleanConstructor; readonly Number: NumberConstructor; readonly Math: Math; readonly Date: DateConstructor; readonly RegExp: RegExpConstructor; readonly Error: ErrorConstructor; readonly EvalError: EvalErrorConstructor; readonly RangeError: RangeErrorConstructor; readonly ReferenceError: ReferenceErrorConstructor; readonly SyntaxError: SyntaxErrorConstructor; readonly TypeError: TypeErrorConstructor; readonly URIError: URIErrorConstructor; readonly JSON: JSON; readonly Array: ArrayConstructor; readonly ArrayBuffer: ArrayBufferConstructor; readonly DataView: DataViewConstructor; readonly Int8Array: Int8ArrayConstructor; readonly Uint8Array: Uint8ArrayConstructor; readonly Uint8ClampedArray: Uint8ClampedArrayConstructor; readonly Int16Array: Int16ArrayConstructor; readonly Uint16Array: Uint16ArrayConstructor; readonly Int32Array: Int32ArrayConstructor; readonly Uint32Array: Uint32ArrayConstructor; readonly Float32Array: Float32ArrayConstructor; readonly Float64Array: Float64ArrayConstructor; Intl: typeof Intl; a: number; undefined: undefined; }
+>unknown : any
+
diff --git a/tests/baselines/reference/topLevelThisAssignment2.errors.txt b/tests/baselines/reference/topLevelThisAssignment2.errors.txt
new file mode 100644
index 0000000000000..c4e372eab82bd
--- /dev/null
+++ b/tests/baselines/reference/topLevelThisAssignment2.errors.txt
@@ -0,0 +1,20 @@
+tests/cases/conformance/salsa/mod.js(6,1): error TS2304: Cannot find name 'a'.
+
+
+==== tests/cases/conformance/salsa/use.js (0 errors) ====
+ var mod = require('./mod')
+ mod.a;
+
+==== tests/cases/conformance/salsa/decl.d.ts (0 errors) ====
+ declare var module: { exports: any };
+ declare function require(name: string): any;
+==== tests/cases/conformance/salsa/mod.js (1 errors) ====
+ ///
+ module.exports = {};
+ this.a = 10;
+ this.a; // ok
+ module.exports.a; // should be ok but doesn't have the right type
+ a; // error: not actually at top-level in a module
+ ~
+!!! error TS2304: Cannot find name 'a'.
+
\ No newline at end of file
diff --git a/tests/baselines/reference/topLevelThisAssignment2.symbols b/tests/baselines/reference/topLevelThisAssignment2.symbols
new file mode 100644
index 0000000000000..6f0eded03efdb
--- /dev/null
+++ b/tests/baselines/reference/topLevelThisAssignment2.symbols
@@ -0,0 +1,44 @@
+=== tests/cases/conformance/salsa/use.js ===
+var mod = require('./mod')
+>mod : Symbol(mod, Decl(use.js, 0, 3))
+>require : Symbol(require, Decl(decl.d.ts, 0, 37))
+>'./mod' : Symbol("tests/cases/conformance/salsa/mod", Decl(mod.js, 0, 0))
+
+mod.a;
+>mod.a : Symbol(a, Decl(mod.js, 1, 20))
+>mod : Symbol(mod, Decl(use.js, 0, 3))
+>a : Symbol(a, Decl(mod.js, 1, 20))
+
+=== tests/cases/conformance/salsa/decl.d.ts ===
+declare var module: { exports: any };
+>module : Symbol(module, Decl(decl.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(decl.d.ts, 0, 21))
+
+declare function require(name: string): any;
+>require : Symbol(require, Decl(decl.d.ts, 0, 37))
+>name : Symbol(name, Decl(decl.d.ts, 1, 25))
+
+=== tests/cases/conformance/salsa/mod.js ===
+///
+module.exports = {};
+>module.exports : Symbol(exports, Decl(decl.d.ts, 0, 21))
+>module : Symbol(module, Decl(decl.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(decl.d.ts, 0, 21))
+
+this.a = 10;
+>this.a : Symbol(a, Decl(mod.js, 1, 20))
+>this : Symbol("tests/cases/conformance/salsa/mod", Decl(mod.js, 0, 0))
+>a : Symbol(a, Decl(mod.js, 1, 20))
+
+this.a; // ok
+>this.a : Symbol(a, Decl(mod.js, 1, 20))
+>this : Symbol("tests/cases/conformance/salsa/mod", Decl(mod.js, 0, 0))
+>a : Symbol(a, Decl(mod.js, 1, 20))
+
+module.exports.a; // should be ok but doesn't have the right type
+>module.exports : Symbol(exports, Decl(decl.d.ts, 0, 21))
+>module : Symbol(module, Decl(decl.d.ts, 0, 11))
+>exports : Symbol(exports, Decl(decl.d.ts, 0, 21))
+
+a; // error: not actually at top-level in a module
+
diff --git a/tests/baselines/reference/topLevelThisAssignment2.types b/tests/baselines/reference/topLevelThisAssignment2.types
new file mode 100644
index 0000000000000..37de14b5b654d
--- /dev/null
+++ b/tests/baselines/reference/topLevelThisAssignment2.types
@@ -0,0 +1,52 @@
+=== tests/cases/conformance/salsa/use.js ===
+var mod = require('./mod')
+>mod : typeof "tests/cases/conformance/salsa/mod"
+>require('./mod') : typeof "tests/cases/conformance/salsa/mod"
+>require : (name: string) => any
+>'./mod' : "./mod"
+
+mod.a;
+>mod.a : number
+>mod : typeof "tests/cases/conformance/salsa/mod"
+>a : number
+
+=== tests/cases/conformance/salsa/decl.d.ts ===
+declare var module: { exports: any };
+>module : { exports: any; }
+>exports : any
+
+declare function require(name: string): any;
+>require : (name: string) => any
+>name : string
+
+=== tests/cases/conformance/salsa/mod.js ===
+///
+module.exports = {};
+>module.exports = {} : { [x: string]: any; }
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>{} : { [x: string]: any; }
+
+this.a = 10;
+>this.a = 10 : 10
+>this.a : number
+>this : typeof "tests/cases/conformance/salsa/mod"
+>a : number
+>10 : 10
+
+this.a; // ok
+>this.a : number
+>this : typeof "tests/cases/conformance/salsa/mod"
+>a : number
+
+module.exports.a; // should be ok but doesn't have the right type
+>module.exports.a : any
+>module.exports : any
+>module : { exports: any; }
+>exports : any
+>a : any
+
+a; // error: not actually at top-level in a module
+>a : any
+
diff --git a/tests/baselines/reference/typeFromPropertyAssignment9.symbols b/tests/baselines/reference/typeFromPropertyAssignment9.symbols
index a4211cdefe70a..742bf83761b2c 100644
--- a/tests/baselines/reference/typeFromPropertyAssignment9.symbols
+++ b/tests/baselines/reference/typeFromPropertyAssignment9.symbols
@@ -117,6 +117,10 @@ min.nest = this.min.nest || function () { };
>min.nest : Symbol(nest, Decl(a.js, 29, 27))
>min : Symbol(min, Decl(a.js, 29, 3))
>nest : Symbol(nest, Decl(a.js, 29, 27))
+>this.min.nest : Symbol(nest, Decl(a.js, 29, 27))
+>this.min : Symbol(min, Decl(a.js, 29, 3))
+>min : Symbol(min, Decl(a.js, 29, 3))
+>nest : Symbol(nest, Decl(a.js, 29, 27))
min.nest.other = self.min.nest.other || class { };
>min.nest.other : Symbol((Anonymous function).other, Decl(a.js, 30, 44))
diff --git a/tests/baselines/reference/typeFromPropertyAssignment9.types b/tests/baselines/reference/typeFromPropertyAssignment9.types
index bd1740fa6e118..b438c68479e2e 100644
--- a/tests/baselines/reference/typeFromPropertyAssignment9.types
+++ b/tests/baselines/reference/typeFromPropertyAssignment9.types
@@ -157,11 +157,11 @@ min.nest = this.min.nest || function () { };
>min : { [x: string]: any; nest: { (): void; other: typeof (Anonymous class); }; property: { [x: string]: any; }; }
>nest : { (): void; other: typeof (Anonymous class); }
>this.min.nest || function () { } : { (): void; other: typeof (Anonymous class); }
->this.min.nest : any
->this.min : any
->this : any
->min : any
->nest : any
+>this.min.nest : { (): void; other: typeof (Anonymous class); }
+>this.min : { [x: string]: any; nest: { (): void; other: typeof (Anonymous class); }; property: { [x: string]: any; }; }
+>this : { [x: string]: any; eval(x: string): any; parseInt(s: string, radix?: number): number; parseFloat(string: string): number; isNaN(number: number): boolean; isFinite(number: number): boolean; decodeURI(encodedURI: string): string; decodeURIComponent(encodedURIComponent: string): string; encodeURI(uri: string): string; encodeURIComponent(uriComponent: string): string; escape(string: string): string; unescape(string: string): string; readonly NaN: number; readonly Infinity: number; readonly Object: ObjectConstructor; readonly Function: FunctionConstructor; readonly String: StringConstructor; readonly Boolean: BooleanConstructor; readonly Number: NumberConstructor; readonly Math: Math; readonly Date: DateConstructor; readonly RegExp: RegExpConstructor; readonly Error: ErrorConstructor; readonly EvalError: EvalErrorConstructor; readonly RangeError: RangeErrorConstructor; readonly ReferenceError: ReferenceErrorConstructor; readonly SyntaxError: SyntaxErrorConstructor; readonly TypeError: TypeErrorConstructor; readonly URIError: URIErrorConstructor; readonly JSON: JSON; readonly Array: ArrayConstructor; Promise: PromiseConstructor; readonly ArrayBuffer: ArrayBufferConstructor; readonly DataView: DataViewConstructor; readonly Int8Array: Int8ArrayConstructor; readonly Uint8Array: Uint8ArrayConstructor; readonly Uint8ClampedArray: Uint8ClampedArrayConstructor; readonly Int16Array: Int16ArrayConstructor; readonly Uint16Array: Uint16ArrayConstructor; readonly Int32Array: Int32ArrayConstructor; readonly Uint32Array: Uint32ArrayConstructor; readonly Float32Array: Float32ArrayConstructor; readonly Float64Array: Float64ArrayConstructor; Intl: typeof Intl; Symbol: SymbolConstructor; Map: MapConstructor; WeakMap: WeakMapConstructor; Set: SetConstructor; WeakSet: WeakSetConstructor; Reflect: typeof Reflect; Proxy: ProxyConstructor; my: { [x: string]: any; method: (n: number) => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; another: () => number; result: string; }; sort: (first: number, second: number) => number; type: typeof (Anonymous class); }; }; q: any; min: { [x: string]: any; nest: { (): void; other: typeof (Anonymous class); }; property: { [x: string]: any; }; }; undefined: undefined; }
+>min : { [x: string]: any; nest: { (): void; other: typeof (Anonymous class); }; property: { [x: string]: any; }; }
+>nest : { (): void; other: typeof (Anonymous class); }
>function () { } : { (): void; other: typeof (Anonymous class); }
min.nest.other = self.min.nest.other || class { };
diff --git a/tests/cases/conformance/salsa/topLevelThisAssignment.ts b/tests/cases/conformance/salsa/topLevelThisAssignment.ts
index 162bed0c30fc7..4d23b0c39a79a 100644
--- a/tests/cases/conformance/salsa/topLevelThisAssignment.ts
+++ b/tests/cases/conformance/salsa/topLevelThisAssignment.ts
@@ -1,10 +1,18 @@
// @out: output.js
+// @checkJs: true
// @allowJs: true
// @Filename: a.js
this.a = 10;
this.a;
a;
+unknown;
+this.unknown;
+
+// also, improved types for this-prefixed globals like eval:
+this.eval('hi');
// @Filename: b.js
this.a;
a;
+unknown;
+this.unknown;
diff --git a/tests/cases/conformance/salsa/topLevelThisAssignment2.ts b/tests/cases/conformance/salsa/topLevelThisAssignment2.ts
new file mode 100644
index 0000000000000..a40534d5092b9
--- /dev/null
+++ b/tests/cases/conformance/salsa/topLevelThisAssignment2.ts
@@ -0,0 +1,17 @@
+// @noEmit: true
+// @allowJs: true
+// @checkJs: true
+// @Filename: decl.d.ts
+declare var module: { exports: any };
+declare function require(name: string): any;
+// @Filename: mod.js
+///
+module.exports = {};
+this.a = 10;
+this.a; // ok
+module.exports.a; // should be ok but doesn't have the right type
+a; // error: not actually at top-level in a module
+
+// @Filename: use.js
+var mod = require('./mod')
+mod.a;
From 2050027a83d1762a7d1ef97458ffd986dfa4c6c2 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 28 Mar 2018 14:13:51 -0700
Subject: [PATCH 4/4] Don't create new types for toplevel every time
1. Add a global type. This is only accessible via toplevel `this` in
javascript scripts, but could be the type of other values later, and
also given a typename later.
2. Use the type of the containing file in the export case.
---
src/compiler/binder.ts | 2 +-
src/compiler/checker.ts | 14 ++++++++++++--
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index fe8910cf5ee00..384538418c7d8 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -2366,7 +2366,7 @@ namespace ts {
// this.foo assignment in a source file
// Bind this property in the global namespace or in the exports if in commonjs
if ((thisContainer as SourceFile).commonJsModuleIndicator) {
- declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None);
+ declareSymbol(thisContainer.symbol.exports, thisContainer.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None);
}
else {
declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 93a10bc7b8a60..37f6e886b50cb 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -419,6 +419,7 @@ namespace ts {
let deferredGlobalAsyncIteratorType: GenericType;
let deferredGlobalAsyncIterableIteratorType: GenericType;
let deferredGlobalTemplateStringsArrayType: ObjectType;
+ let deferredGlobalGlobalType: ObjectType;
let deferredNodes: Node[];
let deferredUnusedIdentifierNodes: Node[];
@@ -7612,6 +7613,10 @@ namespace ts {
return deferredGlobalIterableIteratorType || (deferredGlobalIterableIteratorType = getGlobalType("IterableIterator" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
}
+ function getGlobalGlobalType() {
+ return deferredGlobalGlobalType || (deferredGlobalGlobalType = createAnonymousType(undefined, globals, emptyArray, emptyArray, createIndexInfo(anyType, /*isReadonly*/ false), undefined)) || emptyObjectType;
+ }
+
function getGlobalTypeOrUndefined(name: __String, arity = 0): ObjectType {
const symbol = getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined);
return symbol && getTypeOfGlobalSymbol(symbol, arity);
@@ -13962,8 +13967,13 @@ namespace ts {
}
if (isSourceFile(container)) {
// look up in the source file's locals or exports
- const parent = getSymbolOfNode(container);
- return createAnonymousType(parent, container.commonJsModuleIndicator ? parent.exports : globals, emptyArray, emptyArray, createIndexInfo(anyType, /*isReadonly*/ false), undefined);
+ if (container.commonJsModuleIndicator) {
+ const fileSymbol = getSymbolOfNode(container);
+ return fileSymbol && getTypeOfSymbol(fileSymbol);
+ }
+ else {
+ return getGlobalGlobalType();
+ }
}
}
}