Fix class/constructor-function merge (#27366)

The check for prototype assignment on constructor functions assumes
that the prototype property, if present, comes from an assignment
declaration, such as:

```js
SomeClass.prototype = { /* methods go here */ }
```

In this case, however, when class SomeClass and var SomeClass merge
(because this is allowed), prototype is the synthetic property from
class SomeClass, which has no valueDeclaration.

The fix is to check that prototype has a valueDeclaration before
checking whether the valueDeclaration is in fact a prototype-assignment
declaration.
This commit is contained in:
Nathan Shively-Sanders 2018-10-08 12:56:03 -07:00
parent a6a27e9de9
commit 8a4b6e03ab
4 changed files with 65 additions and 1 deletions

View File

@ -20459,7 +20459,7 @@ namespace ts {
isBinaryExpression(decl.parent) && getSymbolOfNode(decl.parent.left) ||
isVariableDeclaration(decl.parent) && getSymbolOfNode(decl.parent));
const prototype = assignmentSymbol && assignmentSymbol.exports && assignmentSymbol.exports.get("prototype" as __String);
const init = prototype && getAssignedJSPrototype(prototype.valueDeclaration);
const init = prototype && prototype.valueDeclaration && getAssignedJSPrototype(prototype.valueDeclaration);
return init ? checkExpression(init) : undefined;
}

View File

@ -0,0 +1,21 @@
=== tests/cases/conformance/salsa/file1.js ===
var SomeClass = function () {
>SomeClass : Symbol(SomeClass, Decl(file1.js, 0, 3), Decl(file2.js, 0, 0), Decl(file2.js, 0, 19))
this.otherProp = 0;
>otherProp : Symbol(SomeClass.otherProp, Decl(file1.js, 0, 29))
};
new SomeClass();
>SomeClass : Symbol(SomeClass, Decl(file1.js, 0, 3), Decl(file2.js, 0, 0), Decl(file2.js, 0, 19))
=== tests/cases/conformance/salsa/file2.js ===
class SomeClass { }
>SomeClass : Symbol(SomeClass, Decl(file1.js, 0, 3), Decl(file2.js, 0, 0), Decl(file2.js, 0, 19))
SomeClass.prop = 0
>SomeClass.prop : Symbol(SomeClass.prop, Decl(file2.js, 0, 19))
>SomeClass : Symbol(SomeClass, Decl(file1.js, 0, 3), Decl(file2.js, 0, 0), Decl(file2.js, 0, 19))
>prop : Symbol(SomeClass.prop, Decl(file2.js, 0, 19))

View File

@ -0,0 +1,29 @@
=== tests/cases/conformance/salsa/file1.js ===
var SomeClass = function () {
>SomeClass : typeof SomeClass
>function () { this.otherProp = 0;} : typeof SomeClass
this.otherProp = 0;
>this.otherProp = 0 : 0
>this.otherProp : any
>this : any
>otherProp : any
>0 : 0
};
new SomeClass();
>new SomeClass() : SomeClass
>SomeClass : typeof SomeClass
=== tests/cases/conformance/salsa/file2.js ===
class SomeClass { }
>SomeClass : SomeClass
SomeClass.prop = 0
>SomeClass.prop = 0 : 0
>SomeClass.prop : number
>SomeClass : typeof SomeClass
>prop : number
>0 : 0

View File

@ -0,0 +1,14 @@
// @allowJs: true
// @noEmit: true
// @checkJs: true
// @Filename: file1.js
var SomeClass = function () {
this.otherProp = 0;
};
new SomeClass();
// @Filename: file2.js
class SomeClass { }
SomeClass.prop = 0