Skip to content

Commit f9fd496

Browse files
author
Andy Hanson
committed
Allow export = and export default to alias any EntityNameExpression, not just identifiers.
1 parent 97bbbd7 commit f9fd496

12 files changed

+613
-26
lines changed

src/compiler/binder.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,18 +1887,17 @@ namespace ts {
18871887
}
18881888

18891889
function bindExportAssignment(node: ExportAssignment | BinaryExpression) {
1890-
const boundExpression = node.kind === SyntaxKind.ExportAssignment ? (<ExportAssignment>node).expression : (<BinaryExpression>node).right;
18911890
if (!container.symbol || !container.symbol.exports) {
18921891
// Export assignment in some sort of block construct
18931892
bindAnonymousDeclaration(node, SymbolFlags.Alias, getDeclarationName(node));
18941893
}
1895-
else if (boundExpression.kind === SyntaxKind.Identifier && node.kind === SyntaxKind.ExportAssignment) {
1896-
// An export default clause with an identifier exports all meanings of that identifier
1897-
declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Alias, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes);
1898-
}
18991894
else {
1900-
// An export default clause with an expression exports a value
1901-
declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes);
1895+
const flags = node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(<ExportAssignment>node)
1896+
// An export default clause with an EntityNameExpression exports all meanings of that identifier
1897+
? SymbolFlags.Alias
1898+
// An export default clause with any other expression exports a value
1899+
: SymbolFlags.Property;
1900+
declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes);
19021901
}
19031902
}
19041903

src/compiler/checker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,7 @@ namespace ts {
10441044
}
10451045

10461046
function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration {
1047-
return forEach(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined);
1047+
return find(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined);
10481048
}
10491049

10501050
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration): Symbol {
@@ -1175,7 +1175,7 @@ namespace ts {
11751175
}
11761176

11771177
function getTargetOfExportAssignment(node: ExportAssignment): Symbol {
1178-
return resolveEntityName(<Identifier>node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
1178+
return resolveEntityName(<EntityNameExpression>node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
11791179
}
11801180

11811181
function getTargetOfAliasDeclaration(node: Declaration): Symbol {

src/compiler/core.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ namespace ts {
8181
* returns a truthy value, then returns that value.
8282
* If no such value is found, the callback is applied to each element of array and undefined is returned.
8383
*/
84-
export function forEach<T, U>(array: T[], callback: (element: T, index: number) => U): U {
84+
export function forEach<T, U>(array: T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
8585
if (array) {
8686
for (let i = 0, len = array.length; i < len; i++) {
8787
const result = callback(array[i], i);
@@ -93,6 +93,17 @@ namespace ts {
9393
return undefined;
9494
}
9595

96+
/** Like `forEach`, but assumes existence of array and fails if no truthy value is found. */
97+
export function find<T, U>(array: T[], callback: (element: T, index: number) => U | undefined): U {
98+
for (let i = 0, len = array.length; i < len; i++) {
99+
const result = callback(array[i], i);
100+
if (result) {
101+
return result;
102+
}
103+
}
104+
Debug.fail();
105+
}
106+
96107
export function contains<T>(array: T[], value: T): boolean {
97108
if (array) {
98109
for (const v of array) {
@@ -941,7 +952,7 @@ namespace ts {
941952
* [^./] # matches everything up to the first . character (excluding directory seperators)
942953
* (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
943954
*/
944-
const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*";
955+
const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*";
945956
const singleAsteriskRegexFragmentOther = "[^/]*";
946957

947958
export function getRegularExpressionForWildcard(specs: string[], basePath: string, usage: "files" | "directories" | "exclude") {

src/compiler/utilities.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,16 +1693,20 @@ namespace ts {
16931693
// import * as <symbol> from ...
16941694
// import { x as <symbol> } from ...
16951695
// export { x as <symbol> } from ...
1696-
// export = ...
1697-
// export default ...
1696+
// export = <EntityNameExpression>
1697+
// export default <EntityNameExpression>
16981698
export function isAliasSymbolDeclaration(node: Node): boolean {
16991699
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
17001700
node.kind === SyntaxKind.NamespaceExportDeclaration ||
17011701
node.kind === SyntaxKind.ImportClause && !!(<ImportClause>node).name ||
17021702
node.kind === SyntaxKind.NamespaceImport ||
17031703
node.kind === SyntaxKind.ImportSpecifier ||
17041704
node.kind === SyntaxKind.ExportSpecifier ||
1705-
node.kind === SyntaxKind.ExportAssignment && (<ExportAssignment>node).expression.kind === SyntaxKind.Identifier;
1705+
node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(<ExportAssignment>node);
1706+
}
1707+
1708+
export function exportAssignmentIsAlias(node: ExportAssignment): boolean {
1709+
return isEntityNameExpression(node.expression);
17061710
}
17071711

17081712
export function getClassExtendsHeritageClauseElement(node: ClassLikeDeclaration | InterfaceDeclaration) {
Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,76 @@
1-
//// [exportDefaultProperty.ts]
2-
export default "".length
1+
//// [tests/cases/compiler/exportDefaultProperty.ts] ////
32

3+
//// [declarations.d.ts]
4+
// This test is just like exportEqualsProperty, but with `export default`.
5+
6+
declare namespace foo.bar {
7+
export type X = number;
8+
export const X: number;
9+
}
10+
11+
declare module "foobar" {
12+
export default foo.bar;
13+
}
14+
15+
declare module "foobarx" {
16+
export default foo.bar.X;
17+
}
418

5-
//// [exportDefaultProperty.js]
19+
//// [a.ts]
20+
namespace A {
21+
export class B { constructor(b: number) {} }
22+
export namespace B { export const b: number = 0; }
23+
}
24+
export default A.B;
25+
26+
//// [b.ts]
27+
export default "foo".length;
28+
29+
//// [index.ts]
30+
/// <reference path="declarations.d.ts" />
31+
import fooBar from "foobar";
32+
import X = fooBar.X;
33+
import X2 from "foobarx";
34+
const x: X = X;
35+
const x2: X2 = X2;
36+
37+
import B from "./a";
38+
const b: B = new B(B.b);
39+
40+
import fooLength from "./b";
41+
fooLength + 1;
42+
43+
44+
//// [a.js]
645
"use strict";
46+
var A;
47+
(function (A) {
48+
var B = (function () {
49+
function B(b) {
50+
}
51+
return B;
52+
}());
53+
A.B = B;
54+
var B;
55+
(function (B) {
56+
B.b = 0;
57+
})(B = A.B || (A.B = {}));
58+
})(A || (A = {}));
759
exports.__esModule = true;
8-
exports["default"] = "".length;
60+
exports["default"] = A.B;
61+
//// [b.js]
62+
"use strict";
63+
exports.__esModule = true;
64+
exports["default"] = "foo".length;
65+
//// [index.js]
66+
"use strict";
67+
/// <reference path="declarations.d.ts" />
68+
var foobar_1 = require("foobar");
69+
var X = foobar_1["default"].X;
70+
var foobarx_1 = require("foobarx");
71+
var x = X;
72+
var x2 = foobarx_1["default"];
73+
var a_1 = require("./a");
74+
var b = new a_1["default"](a_1["default"].b);
75+
var b_1 = require("./b");
76+
b_1["default"] + 1;
Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,92 @@
1-
=== tests/cases/compiler/exportDefaultProperty.ts ===
2-
export default "".length
3-
>"".length : Symbol(String.length, Decl(lib.d.ts, --, --))
1+
=== tests/cases/compiler/index.ts ===
2+
/// <reference path="declarations.d.ts" />
3+
import fooBar from "foobar";
4+
>fooBar : Symbol(fooBar, Decl(index.ts, 1, 6))
5+
6+
import X = fooBar.X;
7+
>X : Symbol(X, Decl(index.ts, 1, 28))
8+
>fooBar : Symbol(fooBar, Decl(index.ts, 1, 6))
9+
>X : Symbol(fooBar.X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16))
10+
11+
import X2 from "foobarx";
12+
>X2 : Symbol(X2, Decl(index.ts, 3, 6))
13+
14+
const x: X = X;
15+
>x : Symbol(x, Decl(index.ts, 4, 5))
16+
>X : Symbol(X, Decl(index.ts, 1, 28))
17+
>X : Symbol(X, Decl(index.ts, 1, 28))
18+
19+
const x2: X2 = X2;
20+
>x2 : Symbol(x2, Decl(index.ts, 5, 5))
21+
>X2 : Symbol(X2, Decl(index.ts, 3, 6))
22+
>X2 : Symbol(X2, Decl(index.ts, 3, 6))
23+
24+
import B from "./a";
25+
>B : Symbol(B, Decl(index.ts, 7, 6))
26+
27+
const b: B = new B(B.b);
28+
>b : Symbol(b, Decl(index.ts, 8, 5))
29+
>B : Symbol(B, Decl(index.ts, 7, 6))
30+
>B : Symbol(B, Decl(index.ts, 7, 6))
31+
>B.b : Symbol(B.b, Decl(a.ts, 2, 37))
32+
>B : Symbol(B, Decl(index.ts, 7, 6))
33+
>b : Symbol(B.b, Decl(a.ts, 2, 37))
34+
35+
import fooLength from "./b";
36+
>fooLength : Symbol(fooLength, Decl(index.ts, 10, 6))
37+
38+
fooLength + 1;
39+
>fooLength : Symbol(fooLength, Decl(index.ts, 10, 6))
40+
41+
=== tests/cases/compiler/declarations.d.ts ===
42+
// This test is just like exportEqualsProperty, but with `export default`.
43+
44+
declare namespace foo.bar {
45+
>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0))
46+
>bar : Symbol(bar, Decl(declarations.d.ts, 2, 22))
47+
48+
export type X = number;
49+
>X : Symbol(X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16))
50+
51+
export const X: number;
52+
>X : Symbol(X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16))
53+
}
54+
55+
declare module "foobar" {
56+
export default foo.bar;
57+
>foo.bar : Symbol(default, Decl(declarations.d.ts, 2, 22))
58+
>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0))
59+
>bar : Symbol(default, Decl(declarations.d.ts, 2, 22))
60+
}
61+
62+
declare module "foobarx" {
63+
export default foo.bar.X;
64+
>foo.bar.X : Symbol(default, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16))
65+
>foo.bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22))
66+
>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0))
67+
>bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22))
68+
>X : Symbol(default, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16))
69+
}
70+
71+
=== tests/cases/compiler/a.ts ===
72+
namespace A {
73+
>A : Symbol(A, Decl(a.ts, 0, 0))
74+
75+
export class B { constructor(b: number) {} }
76+
>B : Symbol(B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48))
77+
>b : Symbol(b, Decl(a.ts, 1, 33))
78+
79+
export namespace B { export const b: number = 0; }
80+
>B : Symbol(B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48))
81+
>b : Symbol(b, Decl(a.ts, 2, 37))
82+
}
83+
export default A.B;
84+
>A.B : Symbol(default, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48))
85+
>A : Symbol(A, Decl(a.ts, 0, 0))
86+
>B : Symbol(default, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48))
87+
88+
=== tests/cases/compiler/b.ts ===
89+
export default "foo".length;
90+
>"foo".length : Symbol(String.length, Decl(lib.d.ts, --, --))
491
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
592

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,97 @@
1-
=== tests/cases/compiler/exportDefaultProperty.ts ===
2-
export default "".length
3-
>"".length : number
4-
>"" : string
1+
=== tests/cases/compiler/index.ts ===
2+
/// <reference path="declarations.d.ts" />
3+
import fooBar from "foobar";
4+
>fooBar : typeof fooBar
5+
6+
import X = fooBar.X;
7+
>X : number
8+
>fooBar : typeof fooBar
9+
>X : number
10+
11+
import X2 from "foobarx";
12+
>X2 : number
13+
14+
const x: X = X;
15+
>x : number
16+
>X : number
17+
>X : number
18+
19+
const x2: X2 = X2;
20+
>x2 : number
21+
>X2 : number
22+
>X2 : number
23+
24+
import B from "./a";
25+
>B : typeof B
26+
27+
const b: B = new B(B.b);
28+
>b : B
29+
>B : B
30+
>new B(B.b) : B
31+
>B : typeof B
32+
>B.b : number
33+
>B : typeof B
34+
>b : number
35+
36+
import fooLength from "./b";
37+
>fooLength : number
38+
39+
fooLength + 1;
40+
>fooLength + 1 : number
41+
>fooLength : number
42+
>1 : number
43+
44+
=== tests/cases/compiler/declarations.d.ts ===
45+
// This test is just like exportEqualsProperty, but with `export default`.
46+
47+
declare namespace foo.bar {
48+
>foo : typeof foo
49+
>bar : typeof bar
50+
51+
export type X = number;
52+
>X : number
53+
54+
export const X: number;
55+
>X : number
56+
}
57+
58+
declare module "foobar" {
59+
export default foo.bar;
60+
>foo.bar : typeof default
61+
>foo : typeof foo
62+
>bar : typeof default
63+
}
64+
65+
declare module "foobarx" {
66+
export default foo.bar.X;
67+
>foo.bar.X : number
68+
>foo.bar : typeof foo.bar
69+
>foo : typeof foo
70+
>bar : typeof foo.bar
71+
>X : number
72+
}
73+
74+
=== tests/cases/compiler/a.ts ===
75+
namespace A {
76+
>A : typeof A
77+
78+
export class B { constructor(b: number) {} }
79+
>B : B
80+
>b : number
81+
82+
export namespace B { export const b: number = 0; }
83+
>B : typeof B
84+
>b : number
85+
>0 : number
86+
}
87+
export default A.B;
88+
>A.B : typeof default
89+
>A : typeof A
90+
>B : typeof default
91+
92+
=== tests/cases/compiler/b.ts ===
93+
export default "foo".length;
94+
>"foo".length : number
95+
>"foo" : string
596
>length : number
697

0 commit comments

Comments
 (0)