Add constructor functions to aliasable expressions (#36108)

* Add constructor functions to aliasable expressions

Fixes #35228, at least the crash. There are still a couple of errors
that are probably incorrect.

Note that isJSConstructor relies on parent pointers and the ability to
merge symbols, so I had to move isAliasSymbolDeclaration (back?) to the
checker.

* add simple test case

* remove errors in test

* fix bad merge
This commit is contained in:
Nathan Shively-Sanders 2020-01-29 09:36:59 -08:00 committed by GitHub
parent 49282d9fba
commit 0cf100dcf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 294 additions and 30 deletions

View File

@ -2181,6 +2181,43 @@ namespace ts {
return find<Declaration>(symbol.declarations, isAliasSymbolDeclaration);
}
/**
* An alias symbol is created by one of the following declarations:
* import <symbol> = ...
* import <symbol> from ...
* import * as <symbol> from ...
* import { x as <symbol> } from ...
* export { x as <symbol> } from ...
* export * as ns <symbol> from ...
* export = <EntityNameExpression>
* export default <EntityNameExpression>
* module.exports = <EntityNameExpression>
* {<Identifier>}
* {name: <EntityNameExpression>}
*/
function isAliasSymbolDeclaration(node: Node): boolean {
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
node.kind === SyntaxKind.NamespaceExportDeclaration ||
node.kind === SyntaxKind.ImportClause && !!(<ImportClause>node).name ||
node.kind === SyntaxKind.NamespaceImport ||
node.kind === SyntaxKind.NamespaceExport ||
node.kind === SyntaxKind.ImportSpecifier ||
node.kind === SyntaxKind.ExportSpecifier ||
node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(<ExportAssignment>node) ||
isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ModuleExports && exportAssignmentIsAlias(node) ||
isPropertyAccessExpression(node)
&& isBinaryExpression(node.parent)
&& node.parent.left === node
&& node.parent.operatorToken.kind === SyntaxKind.EqualsToken
&& isAliasableOrJsExpression(node.parent.right) ||
node.kind === SyntaxKind.ShorthandPropertyAssignment ||
node.kind === SyntaxKind.PropertyAssignment && isAliasableOrJsExpression((node as PropertyAssignment).initializer);
}
function isAliasableOrJsExpression(e: Expression) {
return isAliasableExpression(e) || isFunctionExpression(e) && isJSConstructor(e);
}
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration, dontResolveAlias: boolean): Symbol | undefined {
if (node.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
return resolveExternalModuleSymbol(resolveExternalModuleName(node, getExternalModuleImportEqualsDeclarationExpression(node)));
@ -5784,8 +5821,8 @@ namespace ts {
function serializeAsAlias(symbol: Symbol, localName: string, modifierFlags: ModifierFlags) {
// synthesize an alias, eg `export { symbolName as Name }`
// need to mark the alias `symbol` points
// at as something we need to serialize as a private declaration as well
// need to mark the alias `symbol` points at
// as something we need to serialize as a private declaration as well
const node = getDeclarationOfAliasSymbol(symbol);
if (!node) return Debug.fail();
const target = getMergedSymbol(getTargetOfAliasDeclaration(node, /*dontRecursivelyResolve*/ true));

View File

@ -2738,33 +2738,6 @@ namespace ts {
return false;
}
// An alias symbol is created by one of the following declarations:
// import <symbol> = ...
// import <symbol> from ...
// import * as <symbol> from ...
// import { x as <symbol> } from ...
// export { x as <symbol> } from ...
// export * as ns <symbol> from ...
// export = <EntityNameExpression>
// export default <EntityNameExpression>
// module.exports = <EntityNameExpression>
// {<Identifier>}
// {name: <EntityNameExpression>}
export function isAliasSymbolDeclaration(node: Node): boolean {
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
node.kind === SyntaxKind.NamespaceExportDeclaration ||
node.kind === SyntaxKind.ImportClause && !!(<ImportClause>node).name ||
node.kind === SyntaxKind.NamespaceImport ||
node.kind === SyntaxKind.NamespaceExport ||
node.kind === SyntaxKind.ImportSpecifier ||
node.kind === SyntaxKind.ExportSpecifier ||
node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(<ExportAssignment>node) ||
isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ModuleExports && exportAssignmentIsAlias(node) ||
isPropertyAccessExpression(node) && isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.EqualsToken && isAliasableExpression(node.parent.right) ||
node.kind === SyntaxKind.ShorthandPropertyAssignment ||
node.kind === SyntaxKind.PropertyAssignment && isAliasableExpression((node as PropertyAssignment).initializer);
}
export function getTypeOnlyCompatibleAliasDeclarationFromName(node: Identifier): TypeOnlyCompatibleAliasDeclaration | undefined {
switch (node.parent.kind) {
case SyntaxKind.ImportClause:
@ -2775,7 +2748,7 @@ namespace ts {
}
}
function isAliasableExpression(e: Expression) {
export function isAliasableExpression(e: Expression) {
return isEntityNameExpression(e) || isClassExpression(e);
}

View File

@ -0,0 +1,24 @@
//// [jsDeclarationsExportAssignedConstructorFunction.js]
/** @constructor */
module.exports.MyClass = function() {
this.x = 1
}
module.exports.MyClass.prototype = {
a: function() {
}
}
//// [jsDeclarationsExportAssignedConstructorFunction.js]
/** @constructor */
module.exports.MyClass = function () {
this.x = 1;
};
module.exports.MyClass.prototype = {
a: function () {
}
};
//// [jsDeclarationsExportAssignedConstructorFunction.d.ts]
export {};

View File

@ -0,0 +1,28 @@
=== tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction.js ===
/** @constructor */
module.exports.MyClass = function() {
>module.exports.MyClass : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 15))
>module.exports : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 15))
>module : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0))
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction", Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0))
>MyClass : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 15))
this.x = 1
>this.x : Symbol(MyClass.x, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 1, 37))
>this : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 1, 24))
>x : Symbol(MyClass.x, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 1, 37))
}
module.exports.MyClass.prototype = {
>module.exports.MyClass.prototype : Symbol(MyClass.prototype, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 3, 1))
>module.exports.MyClass : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 15))
>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction", Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0))
>module : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0))
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction", Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0))
>MyClass : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 15))
>prototype : Symbol(MyClass.prototype, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 3, 1))
a: function() {
>a : Symbol(a, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 36))
}
}

View File

@ -0,0 +1,35 @@
=== tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction.js ===
/** @constructor */
module.exports.MyClass = function() {
>module.exports.MyClass = function() { this.x = 1} : typeof MyClass
>module.exports.MyClass : typeof MyClass
>module.exports : typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction")
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction\"": typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction"); }
>exports : typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction")
>MyClass : typeof MyClass
>function() { this.x = 1} : typeof MyClass
this.x = 1
>this.x = 1 : 1
>this.x : number
>this : this
>x : number
>1 : 1
}
module.exports.MyClass.prototype = {
>module.exports.MyClass.prototype = { a: function() { }} : { a: () => void; }
>module.exports.MyClass.prototype : { a: () => void; }
>module.exports.MyClass : typeof MyClass
>module.exports : typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction")
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction\"": typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction"); }
>exports : typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction")
>MyClass : typeof MyClass
>prototype : { a: () => void; }
>{ a: function() { }} : { a: () => void; }
a: function() {
>a : () => void
>function() { } : () => void
}
}

View File

@ -0,0 +1,23 @@
tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub.js(4,1): error TS9005: Declaration emit for this file requires using private name 'exports'. An explicit type annotation may unblock declaration emit.
tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub.js(5,10): error TS2339: Property 't' does not exist on type '{ "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }'.
tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub.js(8,10): error TS2339: Property 'instance' does not exist on type 'typeof exports'.
==== tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub.js (3 errors) ====
/**
* @param {number} p
*/
module.exports = function (p) {
~~~~~~
!!! error TS9005: Declaration emit for this file requires using private name 'exports'. An explicit type annotation may unblock declaration emit.
this.t = 12 + p;
~
!!! error TS2339: Property 't' does not exist on type '{ "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }'.
}
module.exports.Sub = function() {
this.instance = new module.exports(10);
~~~~~~~~
!!! error TS2339: Property 'instance' does not exist on type 'typeof exports'.
}
module.exports.Sub.prototype = { }

View File

@ -0,0 +1,24 @@
//// [jsDeclarationsExportAssignedConstructorFunctionWithSub.js]
/**
* @param {number} p
*/
module.exports = function (p) {
this.t = 12 + p;
}
module.exports.Sub = function() {
this.instance = new module.exports(10);
}
module.exports.Sub.prototype = { }
//// [jsDeclarationsExportAssignedConstructorFunctionWithSub.js]
/**
* @param {number} p
*/
module.exports = function (p) {
this.t = 12 + p;
};
module.exports.Sub = function () {
this.instance = new module.exports(10);
};
module.exports.Sub.prototype = {};

View File

@ -0,0 +1,38 @@
=== tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub.js ===
/**
* @param {number} p
*/
module.exports = function (p) {
>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
>module : Symbol(export=, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
>exports : Symbol(export=, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
>p : Symbol(p, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 3, 27))
this.t = 12 + p;
>this : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 7, 23))
>t : Symbol(exports.t, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 3, 31))
>p : Symbol(p, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 3, 27))
}
module.exports.Sub = function() {
>module.exports.Sub : Symbol(Sub, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 5, 1), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 9, 15))
>module.exports : Symbol(Sub, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 5, 1), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 9, 15))
>module : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 7, 23))
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
>Sub : Symbol(Sub, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 5, 1), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 9, 15))
this.instance = new module.exports(10);
>this : Symbol(exports, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 3, 16))
>instance : Symbol(Sub.instance, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 6, 33))
>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
>module : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 7, 23))
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
}
module.exports.Sub.prototype = { }
>module.exports.Sub.prototype : Symbol(Sub.prototype, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 8, 1))
>module.exports.Sub : Symbol(Sub, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 5, 1), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 9, 15))
>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
>module : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 7, 23))
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
>Sub : Symbol(Sub, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 5, 1), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 9, 15))
>prototype : Symbol(Sub.prototype, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 8, 1))

View File

@ -0,0 +1,52 @@
=== tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub.js ===
/**
* @param {number} p
*/
module.exports = function (p) {
>module.exports = function (p) { this.t = 12 + p;} : typeof exports
>module.exports : typeof exports
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }
>exports : typeof exports
>function (p) { this.t = 12 + p;} : typeof exports
>p : number
this.t = 12 + p;
>this.t = 12 + p : number
>this.t : any
>this : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }
>t : any
>12 + p : number
>12 : 12
>p : number
}
module.exports.Sub = function() {
>module.exports.Sub = function() { this.instance = new module.exports(10);} : typeof Sub
>module.exports.Sub : typeof Sub
>module.exports : typeof exports
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }
>exports : typeof exports
>Sub : typeof Sub
>function() { this.instance = new module.exports(10);} : typeof Sub
this.instance = new module.exports(10);
>this.instance = new module.exports(10) : exports
>this.instance : any
>this : typeof exports
>instance : any
>new module.exports(10) : exports
>module.exports : typeof exports
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }
>exports : typeof exports
>10 : 10
}
module.exports.Sub.prototype = { }
>module.exports.Sub.prototype = { } : {}
>module.exports.Sub.prototype : {}
>module.exports.Sub : typeof Sub
>module.exports : typeof exports
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }
>exports : typeof exports
>Sub : typeof Sub
>prototype : {}
>{ } : {}

View File

@ -0,0 +1,14 @@
// @allowJs: true
// @checkJs: true
// @target: es5
// @outDir: ./out
// @declaration: true
// @filename: jsDeclarationsExportAssignedConstructorFunction.js
/** @constructor */
module.exports.MyClass = function() {
this.x = 1
}
module.exports.MyClass.prototype = {
a: function() {
}
}

View File

@ -0,0 +1,16 @@
// @allowJs: true
// @checkJs: true
// @target: es5
// @outDir: ./out
// @declaration: true
// @filename: jsDeclarationsExportAssignedConstructorFunctionWithSub.js
/**
* @param {number} p
*/
module.exports = function (p) {
this.t = 12 + p;
}
module.exports.Sub = function() {
this.instance = new module.exports(10);
}
module.exports.Sub.prototype = { }