Skip to content

Commit 0cf100d

Browse files
authored
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
1 parent 49282d9 commit 0cf100d

11 files changed

+294
-30
lines changed

src/compiler/checker.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,43 @@ namespace ts {
21812181
return find<Declaration>(symbol.declarations, isAliasSymbolDeclaration);
21822182
}
21832183

2184+
/**
2185+
* An alias symbol is created by one of the following declarations:
2186+
* import <symbol> = ...
2187+
* import <symbol> from ...
2188+
* import * as <symbol> from ...
2189+
* import { x as <symbol> } from ...
2190+
* export { x as <symbol> } from ...
2191+
* export * as ns <symbol> from ...
2192+
* export = <EntityNameExpression>
2193+
* export default <EntityNameExpression>
2194+
* module.exports = <EntityNameExpression>
2195+
* {<Identifier>}
2196+
* {name: <EntityNameExpression>}
2197+
*/
2198+
function isAliasSymbolDeclaration(node: Node): boolean {
2199+
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
2200+
node.kind === SyntaxKind.NamespaceExportDeclaration ||
2201+
node.kind === SyntaxKind.ImportClause && !!(<ImportClause>node).name ||
2202+
node.kind === SyntaxKind.NamespaceImport ||
2203+
node.kind === SyntaxKind.NamespaceExport ||
2204+
node.kind === SyntaxKind.ImportSpecifier ||
2205+
node.kind === SyntaxKind.ExportSpecifier ||
2206+
node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(<ExportAssignment>node) ||
2207+
isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ModuleExports && exportAssignmentIsAlias(node) ||
2208+
isPropertyAccessExpression(node)
2209+
&& isBinaryExpression(node.parent)
2210+
&& node.parent.left === node
2211+
&& node.parent.operatorToken.kind === SyntaxKind.EqualsToken
2212+
&& isAliasableOrJsExpression(node.parent.right) ||
2213+
node.kind === SyntaxKind.ShorthandPropertyAssignment ||
2214+
node.kind === SyntaxKind.PropertyAssignment && isAliasableOrJsExpression((node as PropertyAssignment).initializer);
2215+
}
2216+
2217+
function isAliasableOrJsExpression(e: Expression) {
2218+
return isAliasableExpression(e) || isFunctionExpression(e) && isJSConstructor(e);
2219+
}
2220+
21842221
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration, dontResolveAlias: boolean): Symbol | undefined {
21852222
if (node.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
21862223
return resolveExternalModuleSymbol(resolveExternalModuleName(node, getExternalModuleImportEqualsDeclarationExpression(node)));
@@ -5784,8 +5821,8 @@ namespace ts {
57845821

57855822
function serializeAsAlias(symbol: Symbol, localName: string, modifierFlags: ModifierFlags) {
57865823
// synthesize an alias, eg `export { symbolName as Name }`
5787-
// need to mark the alias `symbol` points
5788-
// at as something we need to serialize as a private declaration as well
5824+
// need to mark the alias `symbol` points at
5825+
// as something we need to serialize as a private declaration as well
57895826
const node = getDeclarationOfAliasSymbol(symbol);
57905827
if (!node) return Debug.fail();
57915828
const target = getMergedSymbol(getTargetOfAliasDeclaration(node, /*dontRecursivelyResolve*/ true));

src/compiler/utilities.ts

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,33 +2738,6 @@ namespace ts {
27382738
return false;
27392739
}
27402740

2741-
// An alias symbol is created by one of the following declarations:
2742-
// import <symbol> = ...
2743-
// import <symbol> from ...
2744-
// import * as <symbol> from ...
2745-
// import { x as <symbol> } from ...
2746-
// export { x as <symbol> } from ...
2747-
// export * as ns <symbol> from ...
2748-
// export = <EntityNameExpression>
2749-
// export default <EntityNameExpression>
2750-
// module.exports = <EntityNameExpression>
2751-
// {<Identifier>}
2752-
// {name: <EntityNameExpression>}
2753-
export function isAliasSymbolDeclaration(node: Node): boolean {
2754-
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
2755-
node.kind === SyntaxKind.NamespaceExportDeclaration ||
2756-
node.kind === SyntaxKind.ImportClause && !!(<ImportClause>node).name ||
2757-
node.kind === SyntaxKind.NamespaceImport ||
2758-
node.kind === SyntaxKind.NamespaceExport ||
2759-
node.kind === SyntaxKind.ImportSpecifier ||
2760-
node.kind === SyntaxKind.ExportSpecifier ||
2761-
node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(<ExportAssignment>node) ||
2762-
isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ModuleExports && exportAssignmentIsAlias(node) ||
2763-
isPropertyAccessExpression(node) && isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.EqualsToken && isAliasableExpression(node.parent.right) ||
2764-
node.kind === SyntaxKind.ShorthandPropertyAssignment ||
2765-
node.kind === SyntaxKind.PropertyAssignment && isAliasableExpression((node as PropertyAssignment).initializer);
2766-
}
2767-
27682741
export function getTypeOnlyCompatibleAliasDeclarationFromName(node: Identifier): TypeOnlyCompatibleAliasDeclaration | undefined {
27692742
switch (node.parent.kind) {
27702743
case SyntaxKind.ImportClause:
@@ -2775,7 +2748,7 @@ namespace ts {
27752748
}
27762749
}
27772750

2778-
function isAliasableExpression(e: Expression) {
2751+
export function isAliasableExpression(e: Expression) {
27792752
return isEntityNameExpression(e) || isClassExpression(e);
27802753
}
27812754

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [jsDeclarationsExportAssignedConstructorFunction.js]
2+
/** @constructor */
3+
module.exports.MyClass = function() {
4+
this.x = 1
5+
}
6+
module.exports.MyClass.prototype = {
7+
a: function() {
8+
}
9+
}
10+
11+
12+
//// [jsDeclarationsExportAssignedConstructorFunction.js]
13+
/** @constructor */
14+
module.exports.MyClass = function () {
15+
this.x = 1;
16+
};
17+
module.exports.MyClass.prototype = {
18+
a: function () {
19+
}
20+
};
21+
22+
23+
//// [jsDeclarationsExportAssignedConstructorFunction.d.ts]
24+
export {};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction.js ===
2+
/** @constructor */
3+
module.exports.MyClass = function() {
4+
>module.exports.MyClass : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 15))
5+
>module.exports : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 15))
6+
>module : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0))
7+
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction", Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0))
8+
>MyClass : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 15))
9+
10+
this.x = 1
11+
>this.x : Symbol(MyClass.x, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 1, 37))
12+
>this : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 1, 24))
13+
>x : Symbol(MyClass.x, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 1, 37))
14+
}
15+
module.exports.MyClass.prototype = {
16+
>module.exports.MyClass.prototype : Symbol(MyClass.prototype, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 3, 1))
17+
>module.exports.MyClass : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 15))
18+
>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction", Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0))
19+
>module : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0))
20+
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction", Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0))
21+
>MyClass : Symbol(MyClass, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 15))
22+
>prototype : Symbol(MyClass.prototype, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 3, 1))
23+
24+
a: function() {
25+
>a : Symbol(a, Decl(jsDeclarationsExportAssignedConstructorFunction.js, 4, 36))
26+
}
27+
}
28+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction.js ===
2+
/** @constructor */
3+
module.exports.MyClass = function() {
4+
>module.exports.MyClass = function() { this.x = 1} : typeof MyClass
5+
>module.exports.MyClass : typeof MyClass
6+
>module.exports : typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction")
7+
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction\"": typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction"); }
8+
>exports : typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction")
9+
>MyClass : typeof MyClass
10+
>function() { this.x = 1} : typeof MyClass
11+
12+
this.x = 1
13+
>this.x = 1 : 1
14+
>this.x : number
15+
>this : this
16+
>x : number
17+
>1 : 1
18+
}
19+
module.exports.MyClass.prototype = {
20+
>module.exports.MyClass.prototype = { a: function() { }} : { a: () => void; }
21+
>module.exports.MyClass.prototype : { a: () => void; }
22+
>module.exports.MyClass : typeof MyClass
23+
>module.exports : typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction")
24+
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction\"": typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction"); }
25+
>exports : typeof import("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunction")
26+
>MyClass : typeof MyClass
27+
>prototype : { a: () => void; }
28+
>{ a: function() { }} : { a: () => void; }
29+
30+
a: function() {
31+
>a : () => void
32+
>function() { } : () => void
33+
}
34+
}
35+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
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.
2+
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; }'.
3+
tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub.js(8,10): error TS2339: Property 'instance' does not exist on type 'typeof exports'.
4+
5+
6+
==== tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub.js (3 errors) ====
7+
/**
8+
* @param {number} p
9+
*/
10+
module.exports = function (p) {
11+
~~~~~~
12+
!!! error TS9005: Declaration emit for this file requires using private name 'exports'. An explicit type annotation may unblock declaration emit.
13+
this.t = 12 + p;
14+
~
15+
!!! error TS2339: Property 't' does not exist on type '{ "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }'.
16+
}
17+
module.exports.Sub = function() {
18+
this.instance = new module.exports(10);
19+
~~~~~~~~
20+
!!! error TS2339: Property 'instance' does not exist on type 'typeof exports'.
21+
}
22+
module.exports.Sub.prototype = { }
23+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [jsDeclarationsExportAssignedConstructorFunctionWithSub.js]
2+
/**
3+
* @param {number} p
4+
*/
5+
module.exports = function (p) {
6+
this.t = 12 + p;
7+
}
8+
module.exports.Sub = function() {
9+
this.instance = new module.exports(10);
10+
}
11+
module.exports.Sub.prototype = { }
12+
13+
14+
//// [jsDeclarationsExportAssignedConstructorFunctionWithSub.js]
15+
/**
16+
* @param {number} p
17+
*/
18+
module.exports = function (p) {
19+
this.t = 12 + p;
20+
};
21+
module.exports.Sub = function () {
22+
this.instance = new module.exports(10);
23+
};
24+
module.exports.Sub.prototype = {};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=== tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub.js ===
2+
/**
3+
* @param {number} p
4+
*/
5+
module.exports = function (p) {
6+
>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
7+
>module : Symbol(export=, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
8+
>exports : Symbol(export=, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
9+
>p : Symbol(p, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 3, 27))
10+
11+
this.t = 12 + p;
12+
>this : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 7, 23))
13+
>t : Symbol(exports.t, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 3, 31))
14+
>p : Symbol(p, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 3, 27))
15+
}
16+
module.exports.Sub = function() {
17+
>module.exports.Sub : Symbol(Sub, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 5, 1), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 9, 15))
18+
>module.exports : Symbol(Sub, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 5, 1), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 9, 15))
19+
>module : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 7, 23))
20+
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
21+
>Sub : Symbol(Sub, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 5, 1), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 9, 15))
22+
23+
this.instance = new module.exports(10);
24+
>this : Symbol(exports, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 3, 16))
25+
>instance : Symbol(Sub.instance, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 6, 33))
26+
>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
27+
>module : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 7, 23))
28+
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
29+
}
30+
module.exports.Sub.prototype = { }
31+
>module.exports.Sub.prototype : Symbol(Sub.prototype, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 8, 1))
32+
>module.exports.Sub : Symbol(Sub, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 5, 1), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 9, 15))
33+
>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
34+
>module : Symbol(module, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 7, 23))
35+
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub", Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 0, 0))
36+
>Sub : Symbol(Sub, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 5, 1), Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 9, 15))
37+
>prototype : Symbol(Sub.prototype, Decl(jsDeclarationsExportAssignedConstructorFunctionWithSub.js, 8, 1))
38+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
=== tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub.js ===
2+
/**
3+
* @param {number} p
4+
*/
5+
module.exports = function (p) {
6+
>module.exports = function (p) { this.t = 12 + p;} : typeof exports
7+
>module.exports : typeof exports
8+
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }
9+
>exports : typeof exports
10+
>function (p) { this.t = 12 + p;} : typeof exports
11+
>p : number
12+
13+
this.t = 12 + p;
14+
>this.t = 12 + p : number
15+
>this.t : any
16+
>this : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }
17+
>t : any
18+
>12 + p : number
19+
>12 : 12
20+
>p : number
21+
}
22+
module.exports.Sub = function() {
23+
>module.exports.Sub = function() { this.instance = new module.exports(10);} : typeof Sub
24+
>module.exports.Sub : typeof Sub
25+
>module.exports : typeof exports
26+
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }
27+
>exports : typeof exports
28+
>Sub : typeof Sub
29+
>function() { this.instance = new module.exports(10);} : typeof Sub
30+
31+
this.instance = new module.exports(10);
32+
>this.instance = new module.exports(10) : exports
33+
>this.instance : any
34+
>this : typeof exports
35+
>instance : any
36+
>new module.exports(10) : exports
37+
>module.exports : typeof exports
38+
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }
39+
>exports : typeof exports
40+
>10 : 10
41+
}
42+
module.exports.Sub.prototype = { }
43+
>module.exports.Sub.prototype = { } : {}
44+
>module.exports.Sub.prototype : {}
45+
>module.exports.Sub : typeof Sub
46+
>module.exports : typeof exports
47+
>module : { "\"tests/cases/conformance/jsdoc/declarations/jsDeclarationsExportAssignedConstructorFunctionWithSub\"": typeof exports; }
48+
>exports : typeof exports
49+
>Sub : typeof Sub
50+
>prototype : {}
51+
>{ } : {}
52+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @target: es5
4+
// @outDir: ./out
5+
// @declaration: true
6+
// @filename: jsDeclarationsExportAssignedConstructorFunction.js
7+
/** @constructor */
8+
module.exports.MyClass = function() {
9+
this.x = 1
10+
}
11+
module.exports.MyClass.prototype = {
12+
a: function() {
13+
}
14+
}

0 commit comments

Comments
 (0)