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(); + } } } }