From d6250c8342dd2831e6d9414e2c12d426dee1bdae Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 6 Jun 2018 09:13:38 -0700 Subject: [PATCH] Fix circularity error when extending class in same JSContainer (#24710) Do this by not widening properties of an object literal that are 1. JS initialisers 2. and not an object literal These properties have types that will never widen, so the compiler shouldn't ask for the types earlier than it strictly needs to. --- src/compiler/checker.ts | 21 ++++-- .../typeFromPropertyAssignment25.symbols | 62 ++++++++++++++++ .../typeFromPropertyAssignment25.types | 74 +++++++++++++++++++ .../salsa/typeFromPropertyAssignment25.ts | 22 ++++++ 4 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/typeFromPropertyAssignment25.symbols create mode 100644 tests/baselines/reference/typeFromPropertyAssignment25.types create mode 100644 tests/cases/conformance/salsa/typeFromPropertyAssignment25.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 58e097e70c2..ca4407e9007 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12170,6 +12170,19 @@ namespace ts { } function getWidenedProperty(prop: Symbol, context: WideningContext | undefined): Symbol { + if (!(prop.flags & SymbolFlags.Property)) { + // Since get accessors already widen their return value there is no need to + // widen accessor based properties here. + return prop; + } + if (prop.flags & SymbolFlags.JSContainer) { + const node = prop.declarations && first(prop.declarations); + const init = getAssignedJavascriptInitializer(node); + if (init && init.kind !== SyntaxKind.ObjectLiteralExpression) { + // for JS special declarations, the only kind of initializer that will widen is object literals + return prop; + } + } const original = getTypeOfSymbol(prop); const propContext = context && createWideningContext(context, prop.escapedName, /*siblings*/ undefined); const widened = getWidenedTypeWithContext(original, propContext); @@ -12190,9 +12203,7 @@ namespace ts { function getWidenedTypeOfObjectLiteral(type: Type, context: WideningContext | undefined): Type { const members = createSymbolTable(); for (const prop of getPropertiesOfObjectType(type)) { - // Since get accessors already widen their return value there is no need to - // widen accessor based properties here. - members.set(prop.escapedName, prop.flags & SymbolFlags.Property ? getWidenedProperty(prop, context) : prop); + members.set(prop.escapedName, getWidenedProperty(prop, context)); } if (context) { for (const prop of getPropertiesOfContext(context)) { @@ -15984,10 +15995,10 @@ namespace ts { const decl = getDeclarationOfJSInitializer(node); if (decl) { // a JS object literal whose declaration's symbol has exports is a JS namespace - const symbol = getMergedSymbol(decl.symbol); + const symbol = getSymbolOfNode(decl); if (symbol && hasEntries(symbol.exports)) { propertiesTable = symbol.exports; - symbol.exports.forEach(symbol => propertiesArray.push(getMergedSymbol(symbol))); + symbol.exports.forEach(s => propertiesArray.push(getMergedSymbol(s))); return createObjectLiteralType(); } } diff --git a/tests/baselines/reference/typeFromPropertyAssignment25.symbols b/tests/baselines/reference/typeFromPropertyAssignment25.symbols new file mode 100644 index 00000000000..a98333e97d0 --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment25.symbols @@ -0,0 +1,62 @@ +=== tests/cases/conformance/salsa/bug24703.js === +var Common = {}; +>Common : Symbol(Common, Decl(bug24703.js, 0, 3), Decl(bug24703.js, 0, 16)) + +Common.I = class { +>Common.I : Symbol(Common.I, Decl(bug24703.js, 0, 16)) +>Common : Symbol(Common, Decl(bug24703.js, 0, 3), Decl(bug24703.js, 0, 16)) +>I : Symbol(Common.I, Decl(bug24703.js, 0, 16)) + + constructor() { + this.i = 1 +>this.i : Symbol(I.i, Decl(bug24703.js, 2, 19)) +>this : Symbol(I, Decl(bug24703.js, 1, 10)) +>i : Symbol(I.i, Decl(bug24703.js, 2, 19)) + } +} +Common.O = class extends Common.I { +>Common.O : Symbol(Common.O, Decl(bug24703.js, 5, 1)) +>Common : Symbol(Common, Decl(bug24703.js, 0, 3), Decl(bug24703.js, 0, 16)) +>O : Symbol(Common.O, Decl(bug24703.js, 5, 1)) +>Common.I : Symbol(Common.I, Decl(bug24703.js, 0, 16)) +>Common : Symbol(Common, Decl(bug24703.js, 0, 3), Decl(bug24703.js, 0, 16)) +>I : Symbol(Common.I, Decl(bug24703.js, 0, 16)) + + constructor() { + super() +>super : Symbol(I, Decl(bug24703.js, 1, 10)) + + this.o = 2 +>this.o : Symbol(O.o, Decl(bug24703.js, 8, 15)) +>this : Symbol(O, Decl(bug24703.js, 6, 10)) +>o : Symbol(O.o, Decl(bug24703.js, 8, 15)) + } +} +var o = new Common.O() +>o : Symbol(o, Decl(bug24703.js, 12, 3)) +>Common.O : Symbol(Common.O, Decl(bug24703.js, 5, 1)) +>Common : Symbol(Common, Decl(bug24703.js, 0, 3), Decl(bug24703.js, 0, 16)) +>O : Symbol(Common.O, Decl(bug24703.js, 5, 1)) + +var i = new Common.I() +>i : Symbol(i, Decl(bug24703.js, 13, 3)) +>Common.I : Symbol(Common.I, Decl(bug24703.js, 0, 16)) +>Common : Symbol(Common, Decl(bug24703.js, 0, 3), Decl(bug24703.js, 0, 16)) +>I : Symbol(Common.I, Decl(bug24703.js, 0, 16)) + +o.i +>o.i : Symbol(I.i, Decl(bug24703.js, 2, 19)) +>o : Symbol(o, Decl(bug24703.js, 12, 3)) +>i : Symbol(I.i, Decl(bug24703.js, 2, 19)) + +o.o +>o.o : Symbol(O.o, Decl(bug24703.js, 8, 15)) +>o : Symbol(o, Decl(bug24703.js, 12, 3)) +>o : Symbol(O.o, Decl(bug24703.js, 8, 15)) + +i.i +>i.i : Symbol(I.i, Decl(bug24703.js, 2, 19)) +>i : Symbol(i, Decl(bug24703.js, 13, 3)) +>i : Symbol(I.i, Decl(bug24703.js, 2, 19)) + + diff --git a/tests/baselines/reference/typeFromPropertyAssignment25.types b/tests/baselines/reference/typeFromPropertyAssignment25.types new file mode 100644 index 00000000000..e5c4a2066e7 --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment25.types @@ -0,0 +1,74 @@ +=== tests/cases/conformance/salsa/bug24703.js === +var Common = {}; +>Common : { [x: string]: any; I: typeof I; O: typeof O; } +>{} : { [x: string]: any; I: typeof I; O: typeof O; } + +Common.I = class { +>Common.I = class { constructor() { this.i = 1 }} : typeof I +>Common.I : typeof I +>Common : { [x: string]: any; I: typeof I; O: typeof O; } +>I : typeof I +>class { constructor() { this.i = 1 }} : typeof I + + constructor() { + this.i = 1 +>this.i = 1 : 1 +>this.i : number +>this : this +>i : number +>1 : 1 + } +} +Common.O = class extends Common.I { +>Common.O = class extends Common.I { constructor() { super() this.o = 2 }} : typeof O +>Common.O : typeof O +>Common : { [x: string]: any; I: typeof I; O: typeof O; } +>O : typeof O +>class extends Common.I { constructor() { super() this.o = 2 }} : typeof O +>Common.I : I +>Common : { [x: string]: any; I: typeof I; O: typeof O; } +>I : typeof I + + constructor() { + super() +>super() : void +>super : typeof I + + this.o = 2 +>this.o = 2 : 2 +>this.o : number +>this : this +>o : number +>2 : 2 + } +} +var o = new Common.O() +>o : O +>new Common.O() : O +>Common.O : typeof O +>Common : { [x: string]: any; I: typeof I; O: typeof O; } +>O : typeof O + +var i = new Common.I() +>i : I +>new Common.I() : I +>Common.I : typeof I +>Common : { [x: string]: any; I: typeof I; O: typeof O; } +>I : typeof I + +o.i +>o.i : number +>o : O +>i : number + +o.o +>o.o : number +>o : O +>o : number + +i.i +>i.i : number +>i : I +>i : number + + diff --git a/tests/cases/conformance/salsa/typeFromPropertyAssignment25.ts b/tests/cases/conformance/salsa/typeFromPropertyAssignment25.ts new file mode 100644 index 00000000000..37c24366da9 --- /dev/null +++ b/tests/cases/conformance/salsa/typeFromPropertyAssignment25.ts @@ -0,0 +1,22 @@ +// @noEmit: true +// @checkJs: true +// @allowJs: true +// @Filename: bug24703.js +var Common = {}; +Common.I = class { + constructor() { + this.i = 1 + } +} +Common.O = class extends Common.I { + constructor() { + super() + this.o = 2 + } +} +var o = new Common.O() +var i = new Common.I() +o.i +o.o +i.i +