From 03653934c30f4bc3a94f7067ff8506fcdfa37048 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 23 Aug 2018 08:21:28 -0700 Subject: [PATCH] Don't create expando object literals in TS (#26525) Previously, getWidenedTypedFromJSPropertyAssignment was not called for Typescript code. Since property assignments on functions, it is. That meant that property assignments would incorrectly create a JS container for empty object literals in a property assignment, even in Typescript: ```ts const one = () => 1 one.p = {} one.p.q = {} // should not work in Typescript! ``` Now empty object literals never create expando objects in Typescript, because getJSExpandoObjectType requires the declaration to be in a JS file. --- src/compiler/checker.ts | 2 +- .../reference/typeFromPropertyAssignment30.js | 15 +++++++++++++ .../typeFromPropertyAssignment30.symbols | 20 ++++++++++++++++++ .../typeFromPropertyAssignment30.types | 21 +++++++++++++++++++ .../salsa/typeFromPropertyAssignment30.ts | 8 +++++++ 5 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/typeFromPropertyAssignment30.js create mode 100644 tests/baselines/reference/typeFromPropertyAssignment30.symbols create mode 100644 tests/baselines/reference/typeFromPropertyAssignment30.types create mode 100644 tests/cases/conformance/salsa/typeFromPropertyAssignment30.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7e0e7122404..269f8b26f0a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4795,7 +4795,7 @@ namespace ts { } function getJSExpandoObjectType(decl: Node, symbol: Symbol, init: Expression | undefined): Type | undefined { - if (!init || !isObjectLiteralExpression(init) || init.properties.length) { + if (!isInJavaScriptFile(decl) || !init || !isObjectLiteralExpression(init) || init.properties.length) { return undefined; } const exports = createSymbolTable(); diff --git a/tests/baselines/reference/typeFromPropertyAssignment30.js b/tests/baselines/reference/typeFromPropertyAssignment30.js new file mode 100644 index 00000000000..1ec7cf70a8b --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment30.js @@ -0,0 +1,15 @@ +//// [typeFromPropertyAssignment30.ts] +interface Combo { + (): number; + p?: { [s: string]: number }; +} +const c: Combo = () => 1 +// should not be an expando object, but contextually typed by Combo.p +c.p = {} + + + +//// [typeFromPropertyAssignment30.js] +var c = function () { return 1; }; +// should not be an expando object, but contextually typed by Combo.p +c.p = {}; diff --git a/tests/baselines/reference/typeFromPropertyAssignment30.symbols b/tests/baselines/reference/typeFromPropertyAssignment30.symbols new file mode 100644 index 00000000000..5614067864c --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment30.symbols @@ -0,0 +1,20 @@ +=== tests/cases/conformance/salsa/typeFromPropertyAssignment30.ts === +interface Combo { +>Combo : Symbol(Combo, Decl(typeFromPropertyAssignment30.ts, 0, 0)) + + (): number; + p?: { [s: string]: number }; +>p : Symbol(Combo.p, Decl(typeFromPropertyAssignment30.ts, 1, 15)) +>s : Symbol(s, Decl(typeFromPropertyAssignment30.ts, 2, 11)) +} +const c: Combo = () => 1 +>c : Symbol(c, Decl(typeFromPropertyAssignment30.ts, 4, 5), Decl(typeFromPropertyAssignment30.ts, 4, 24)) +>Combo : Symbol(Combo, Decl(typeFromPropertyAssignment30.ts, 0, 0)) + +// should not be an expando object, but contextually typed by Combo.p +c.p = {} +>c.p : Symbol(Combo.p, Decl(typeFromPropertyAssignment30.ts, 1, 15)) +>c : Symbol(c, Decl(typeFromPropertyAssignment30.ts, 4, 5), Decl(typeFromPropertyAssignment30.ts, 4, 24)) +>p : Symbol(Combo.p, Decl(typeFromPropertyAssignment30.ts, 1, 15)) + + diff --git a/tests/baselines/reference/typeFromPropertyAssignment30.types b/tests/baselines/reference/typeFromPropertyAssignment30.types new file mode 100644 index 00000000000..ee2c9fdb8c5 --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment30.types @@ -0,0 +1,21 @@ +=== tests/cases/conformance/salsa/typeFromPropertyAssignment30.ts === +interface Combo { + (): number; + p?: { [s: string]: number }; +>p : { [s: string]: number; } +>s : string +} +const c: Combo = () => 1 +>c : Combo +>() => 1 : { (): number; p: {}; } +>1 : 1 + +// should not be an expando object, but contextually typed by Combo.p +c.p = {} +>c.p = {} : {} +>c.p : { [s: string]: number; } +>c : Combo +>p : { [s: string]: number; } +>{} : {} + + diff --git a/tests/cases/conformance/salsa/typeFromPropertyAssignment30.ts b/tests/cases/conformance/salsa/typeFromPropertyAssignment30.ts new file mode 100644 index 00000000000..e24c8ec2b94 --- /dev/null +++ b/tests/cases/conformance/salsa/typeFromPropertyAssignment30.ts @@ -0,0 +1,8 @@ +interface Combo { + (): number; + p?: { [s: string]: number }; +} +const c: Combo = () => 1 +// should not be an expando object, but contextually typed by Combo.p +c.p = {} +