Restore delayed merge check to getTypeFromJSDocValueReference

This is needed when a function merges with a prototype assignment. The
resulting *merged* symbol is a constructor function marked with
SymbolFlags.Class. However, the merge doesn't happen until
getTypeOfFuncClassEnumModule is called, which, in the
getTypeReferenceType code path, doesn't happen until
getTypeFromJSDocValueReference. That means the check for
SymbolFlags.Class is missed.

Previously, getTypeFromJSDocValueReference had a weird check
`symbol !== getTypeOfSymbol(symbol).symbol`, which, if true, ran
getTypeReferenceType again on `getTypeOfSymbol(symbol).symbol`. For
JS "aliases", this had the effect of dereferencing the alias, and for
function-prototype merges, this had the effect of ... just trying again
after the merge had happened.

This is a confusing way to run things. getTypeReferenceType should
instead detect a function-prototype merge, cause it to happen, and
*then* run the rest of its code instead of relying on try-again logic at
the very end. However, for the RC, I want to fix this code by restoring
the old check, with an additional check to make sure that #33106 doesn't
break again:

```ts
const valueType = getTypeOfSymbol(symbol)
symbol !== valueType.symbol && getMergedSymbol(symbol) === valueType.symbol
```

I'll work on the real fix afterwards and put it into 3.8.
This commit is contained in:
Nathan Shively-Sanders
2019-10-24 08:37:10 -07:00
parent 07d3a2ec7e
commit 056a656937
4 changed files with 91 additions and 1 deletions
+3 -1
View File
@@ -10766,7 +10766,9 @@ namespace ts {
&& isCallExpression(decl.initializer)
&& isRequireCall(decl.initializer, /*requireStringLiteralLikeArgument*/ true)
&& valueType.symbol;
if (isRequireAlias || node.kind === SyntaxKind.ImportType) {
const isImportType = node.kind === SyntaxKind.ImportType;
const isDelayedMergeClass = symbol !== valueType.symbol && getMergedSymbol(symbol) === valueType.symbol;
if (isRequireAlias || isImportType || isDelayedMergeClass) {
typeType = getTypeReferenceType(node, valueType.symbol);
}
}
@@ -0,0 +1,33 @@
=== tests/cases/conformance/jsdoc/test.js ===
/** @param {Workspace.Project} p */
function demo(p) {
>demo : Symbol(demo, Decl(test.js, 0, 0))
>p : Symbol(p, Decl(test.js, 1, 14))
p.isServiceProject()
>p.isServiceProject : Symbol(isServiceProject, Decl(mod1.js, 3, 31))
>p : Symbol(p, Decl(test.js, 1, 14))
>isServiceProject : Symbol(isServiceProject, Decl(mod1.js, 3, 31))
}
=== tests/cases/conformance/jsdoc/mod1.js ===
// Note: mod1.js needs to appear second to trigger the bug
var Workspace = {}
>Workspace : Symbol(Workspace, Decl(mod1.js, 1, 3), Decl(mod1.js, 1, 18), Decl(mod1.js, 2, 37))
Workspace.Project = function wp() { }
>Workspace.Project : Symbol(Workspace.Project, Decl(mod1.js, 1, 18), Decl(mod1.js, 3, 10))
>Workspace : Symbol(Workspace, Decl(mod1.js, 1, 3), Decl(mod1.js, 1, 18), Decl(mod1.js, 2, 37))
>Project : Symbol(Workspace.Project, Decl(mod1.js, 1, 18), Decl(mod1.js, 3, 10))
>wp : Symbol(wp, Decl(mod1.js, 2, 19))
Workspace.Project.prototype = {
>Workspace.Project.prototype : Symbol(Workspace.Project.prototype, Decl(mod1.js, 2, 37))
>Workspace.Project : Symbol(Workspace.Project, Decl(mod1.js, 1, 18), Decl(mod1.js, 3, 10))
>Workspace : Symbol(Workspace, Decl(mod1.js, 1, 3), Decl(mod1.js, 1, 18), Decl(mod1.js, 2, 37))
>Project : Symbol(Workspace.Project, Decl(mod1.js, 1, 18), Decl(mod1.js, 3, 10))
>prototype : Symbol(Workspace.Project.prototype, Decl(mod1.js, 2, 37))
isServiceProject() {}
>isServiceProject : Symbol(isServiceProject, Decl(mod1.js, 3, 31))
}
@@ -0,0 +1,39 @@
=== tests/cases/conformance/jsdoc/test.js ===
/** @param {Workspace.Project} p */
function demo(p) {
>demo : (p: wp) => void
>p : wp
p.isServiceProject()
>p.isServiceProject() : void
>p.isServiceProject : () => void
>p : wp
>isServiceProject : () => void
}
=== tests/cases/conformance/jsdoc/mod1.js ===
// Note: mod1.js needs to appear second to trigger the bug
var Workspace = {}
>Workspace : typeof Workspace
>{} : {}
Workspace.Project = function wp() { }
>Workspace.Project = function wp() { } : typeof wp
>Workspace.Project : typeof wp
>Workspace : typeof Workspace
>Project : typeof wp
>function wp() { } : typeof wp
>wp : typeof wp
Workspace.Project.prototype = {
>Workspace.Project.prototype = { isServiceProject() {}} : { isServiceProject(): void; }
>Workspace.Project.prototype : { isServiceProject(): void; }
>Workspace.Project : typeof wp
>Workspace : typeof Workspace
>Project : typeof wp
>prototype : { isServiceProject(): void; }
>{ isServiceProject() {}} : { isServiceProject(): void; }
isServiceProject() {}
>isServiceProject : () => void
}
@@ -0,0 +1,16 @@
// @noEmit: true
// @allowJs: true
// @checkJs: true
// @Filename: test.js
/** @param {Workspace.Project} p */
function demo(p) {
p.isServiceProject()
}
// @Filename: mod1.js
// Note: mod1.js needs to appear second to trigger the bug
var Workspace = {}
Workspace.Project = function wp() { }
Workspace.Project.prototype = {
isServiceProject() {}
}