Skip to content

Commit 39216e8

Browse files
committed
Merge pull request #6742 from Microsoft/augmentExportEquals
allow augmentation for entities exported via 'export='
2 parents ba0a7c7 + ac528cc commit 39216e8

38 files changed

+1709
-11
lines changed

src/compiler/checker.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -395,10 +395,17 @@ namespace ts {
395395
if (!mainModule) {
396396
return;
397397
}
398-
// if module symbol has already been merged - it is safe to use it.
399-
// otherwise clone it
400-
mainModule = mainModule.flags & SymbolFlags.Merged ? mainModule : cloneSymbol(mainModule);
401-
mergeSymbol(mainModule, moduleAugmentation.symbol);
398+
// obtain item referenced by 'export='
399+
mainModule = resolveExternalModuleSymbol(mainModule);
400+
if (mainModule.flags & SymbolFlags.Namespace) {
401+
// if module symbol has already been merged - it is safe to use it.
402+
// otherwise clone it
403+
mainModule = mainModule.flags & SymbolFlags.Merged ? mainModule : cloneSymbol(mainModule);
404+
mergeSymbol(mainModule, moduleAugmentation.symbol);
405+
}
406+
else {
407+
error(moduleName, Diagnostics.Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, moduleName.text);
408+
}
402409
}
403410
}
404411

@@ -891,7 +898,7 @@ namespace ts {
891898
error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol));
892899
}
893900
else if (!exportDefaultSymbol && allowSyntheticDefaultImports) {
894-
return resolveSymbol(moduleSymbol.exports["export="]) || resolveSymbol(moduleSymbol);
901+
return resolveExternalModuleSymbol(moduleSymbol) || resolveSymbol(moduleSymbol);
895902
}
896903
return exportDefaultSymbol;
897904
}
@@ -1182,7 +1189,7 @@ namespace ts {
11821189
// An external module with an 'export =' declaration resolves to the target of the 'export =' declaration,
11831190
// and an external module with no 'export =' declaration resolves to the module itself.
11841191
function resolveExternalModuleSymbol(moduleSymbol: Symbol): Symbol {
1185-
return moduleSymbol && resolveSymbol(moduleSymbol.exports["export="]) || moduleSymbol;
1192+
return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports["export="])) || moduleSymbol;
11861193
}
11871194

11881195
// An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export ='
@@ -1197,8 +1204,8 @@ namespace ts {
11971204
return symbol;
11981205
}
11991206

1200-
function getExportAssignmentSymbol(moduleSymbol: Symbol): Symbol {
1201-
return moduleSymbol.exports["export="];
1207+
function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean {
1208+
return moduleSymbol.exports["export="] !== undefined;
12021209
}
12031210

12041211
function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] {
@@ -14866,7 +14873,7 @@ namespace ts {
1486614873
else {
1486714874
// export * from "foo"
1486814875
const moduleSymbol = resolveExternalModuleName(node, node.moduleSpecifier);
14869-
if (moduleSymbol && moduleSymbol.exports["export="]) {
14876+
if (moduleSymbol && hasExportAssignmentSymbol(moduleSymbol)) {
1487014877
error(node.moduleSpecifier, Diagnostics.Module_0_uses_export_and_cannot_be_used_with_export_Asterisk, symbolToString(moduleSymbol));
1487114878
}
1487214879
}
@@ -15704,7 +15711,7 @@ namespace ts {
1570415711
return true;
1570515712
}
1570615713

15707-
const hasExportAssignment = getExportAssignmentSymbol(moduleSymbol) !== undefined;
15714+
const hasExportAssignment = hasExportAssignmentSymbol(moduleSymbol);
1570815715
// if module has export assignment then 'resolveExternalModuleSymbol' will return resolved symbol for export assignment
1570915716
// otherwise it will return moduleSymbol itself
1571015717
moduleSymbol = resolveExternalModuleSymbol(moduleSymbol);
@@ -16063,7 +16070,7 @@ namespace ts {
1606316070
if (!isExternalOrCommonJsModule(file)) {
1606416071
mergeSymbolTable(globals, file.locals);
1606516072
}
16066-
if (file.moduleAugmentations) {
16073+
if (file.moduleAugmentations.length) {
1606716074
(augmentations || (augmentations = [])).push(file.moduleAugmentations);
1606816075
}
1606916076
});

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,6 +1819,10 @@
18191819
"category": "Error",
18201820
"code": 2670
18211821
},
1822+
"Cannot augment module '{0}' because it resolves to a non-module entity.": {
1823+
"category": "Error",
1824+
"code": 2671
1825+
},
18221826
"Import declaration '{0}' is using private name '{1}'.": {
18231827
"category": "Error",
18241828
"code": 4000
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
tests/cases/compiler/file2.ts(6,16): error TS2671: Cannot augment module './file1' because it resolves to a non-module entity.
2+
tests/cases/compiler/file3.ts(3,8): error TS2503: Cannot find namespace 'x'.
3+
4+
5+
==== tests/cases/compiler/file3.ts (1 errors) ====
6+
import x = require("./file1");
7+
import "./file2";
8+
let a: x.A; // should not work
9+
~
10+
!!! error TS2503: Cannot find namespace 'x'.
11+
==== tests/cases/compiler/file1.ts (0 errors) ====
12+
var x = 1;
13+
export = x;
14+
15+
==== tests/cases/compiler/file2.ts (1 errors) ====
16+
17+
import x = require("./file1");
18+
19+
// augmentation for './file1'
20+
// should error since './file1' does not have namespace meaning
21+
declare module "./file1" {
22+
~~~~~~~~~
23+
!!! error TS2671: Cannot augment module './file1' because it resolves to a non-module entity.
24+
interface A { a }
25+
}
26+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//// [tests/cases/compiler/augmentExportEquals1.ts] ////
2+
3+
//// [file1.ts]
4+
var x = 1;
5+
export = x;
6+
7+
//// [file2.ts]
8+
9+
import x = require("./file1");
10+
11+
// augmentation for './file1'
12+
// should error since './file1' does not have namespace meaning
13+
declare module "./file1" {
14+
interface A { a }
15+
}
16+
17+
//// [file3.ts]
18+
import x = require("./file1");
19+
import "./file2";
20+
let a: x.A; // should not work
21+
22+
//// [file1.js]
23+
define(["require", "exports"], function (require, exports) {
24+
"use strict";
25+
var x = 1;
26+
return x;
27+
});
28+
//// [file2.js]
29+
define(["require", "exports"], function (require, exports) {
30+
"use strict";
31+
});
32+
//// [file3.js]
33+
define(["require", "exports", "./file2"], function (require, exports) {
34+
"use strict";
35+
var a; // should not work
36+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
tests/cases/compiler/file2.ts(6,16): error TS2671: Cannot augment module 'file1' because it resolves to a non-module entity.
2+
tests/cases/compiler/file3.ts(3,8): error TS2503: Cannot find namespace 'x'.
3+
4+
5+
==== tests/cases/compiler/file3.ts (1 errors) ====
6+
import x = require("file1");
7+
import "file2";
8+
let a: x.A; // should not work
9+
~
10+
!!! error TS2503: Cannot find namespace 'x'.
11+
==== tests/cases/compiler/file1.d.ts (0 errors) ====
12+
13+
declare module "file1" {
14+
var x: number;
15+
export = x;
16+
}
17+
18+
==== tests/cases/compiler/file2.ts (1 errors) ====
19+
/// <reference path="file1.d.ts"/>
20+
import x = require("file1");
21+
22+
// augmentation for 'file1'
23+
// should error since 'file1' does not have namespace meaning
24+
declare module "file1" {
25+
~~~~~~~
26+
!!! error TS2671: Cannot augment module 'file1' because it resolves to a non-module entity.
27+
interface A { a }
28+
}
29+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [tests/cases/compiler/augmentExportEquals1_1.ts] ////
2+
3+
//// [file1.d.ts]
4+
5+
declare module "file1" {
6+
var x: number;
7+
export = x;
8+
}
9+
10+
//// [file2.ts]
11+
/// <reference path="file1.d.ts"/>
12+
import x = require("file1");
13+
14+
// augmentation for 'file1'
15+
// should error since 'file1' does not have namespace meaning
16+
declare module "file1" {
17+
interface A { a }
18+
}
19+
20+
//// [file3.ts]
21+
import x = require("file1");
22+
import "file2";
23+
let a: x.A; // should not work
24+
25+
//// [file2.js]
26+
define(["require", "exports"], function (require, exports) {
27+
"use strict";
28+
});
29+
//// [file3.js]
30+
define(["require", "exports", "file2"], function (require, exports) {
31+
"use strict";
32+
var a; // should not work
33+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
tests/cases/compiler/file2.ts(4,16): error TS2671: Cannot augment module './file1' because it resolves to a non-module entity.
2+
tests/cases/compiler/file3.ts(3,8): error TS2503: Cannot find namespace 'x'.
3+
4+
5+
==== tests/cases/compiler/file3.ts (1 errors) ====
6+
import x = require("./file1");
7+
import "./file2";
8+
let a: x.A; // should not work
9+
~
10+
!!! error TS2503: Cannot find namespace 'x'.
11+
==== tests/cases/compiler/file1.ts (0 errors) ====
12+
13+
function foo() {}
14+
export = foo;
15+
16+
==== tests/cases/compiler/file2.ts (1 errors) ====
17+
import x = require("./file1");
18+
19+
// should error since './file1' does not have namespace meaning
20+
declare module "./file1" {
21+
~~~~~~~~~
22+
!!! error TS2671: Cannot augment module './file1' because it resolves to a non-module entity.
23+
interface A { a }
24+
}
25+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//// [tests/cases/compiler/augmentExportEquals2.ts] ////
2+
3+
//// [file1.ts]
4+
5+
function foo() {}
6+
export = foo;
7+
8+
//// [file2.ts]
9+
import x = require("./file1");
10+
11+
// should error since './file1' does not have namespace meaning
12+
declare module "./file1" {
13+
interface A { a }
14+
}
15+
16+
//// [file3.ts]
17+
import x = require("./file1");
18+
import "./file2";
19+
let a: x.A; // should not work
20+
21+
//// [file1.js]
22+
define(["require", "exports"], function (require, exports) {
23+
"use strict";
24+
function foo() { }
25+
return foo;
26+
});
27+
//// [file2.js]
28+
define(["require", "exports"], function (require, exports) {
29+
"use strict";
30+
});
31+
//// [file3.js]
32+
define(["require", "exports", "./file2"], function (require, exports) {
33+
"use strict";
34+
var a; // should not work
35+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
tests/cases/compiler/file2.ts(6,16): error TS2671: Cannot augment module 'file1' because it resolves to a non-module entity.
2+
tests/cases/compiler/file3.ts(3,8): error TS2503: Cannot find namespace 'x'.
3+
4+
5+
==== tests/cases/compiler/file3.ts (1 errors) ====
6+
import x = require("file1");
7+
import "file2";
8+
let a: x.A; // should not work
9+
~
10+
!!! error TS2503: Cannot find namespace 'x'.
11+
==== tests/cases/compiler/file1.d.ts (0 errors) ====
12+
13+
declare module "file1" {
14+
function foo(): void;
15+
export = foo;
16+
}
17+
18+
==== tests/cases/compiler/file2.ts (1 errors) ====
19+
20+
/// <reference path="file1.d.ts"/>
21+
import x = require("file1");
22+
23+
// should error since './file1' does not have namespace meaning
24+
declare module "file1" {
25+
~~~~~~~
26+
!!! error TS2671: Cannot augment module 'file1' because it resolves to a non-module entity.
27+
interface A { a }
28+
}
29+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [tests/cases/compiler/augmentExportEquals2_1.ts] ////
2+
3+
//// [file1.d.ts]
4+
5+
declare module "file1" {
6+
function foo(): void;
7+
export = foo;
8+
}
9+
10+
//// [file2.ts]
11+
12+
/// <reference path="file1.d.ts"/>
13+
import x = require("file1");
14+
15+
// should error since './file1' does not have namespace meaning
16+
declare module "file1" {
17+
interface A { a }
18+
}
19+
20+
//// [file3.ts]
21+
import x = require("file1");
22+
import "file2";
23+
let a: x.A; // should not work
24+
25+
//// [file2.js]
26+
define(["require", "exports"], function (require, exports) {
27+
"use strict";
28+
});
29+
//// [file3.js]
30+
define(["require", "exports", "file2"], function (require, exports) {
31+
"use strict";
32+
var a; // should not work
33+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
tests/cases/compiler/file2.ts(6,15): error TS2665: Module augmentation cannot introduce new names in the top level scope.
2+
tests/cases/compiler/file2.ts(7,9): error TS2665: Module augmentation cannot introduce new names in the top level scope.
3+
4+
5+
==== tests/cases/compiler/file1.ts (0 errors) ====
6+
7+
function foo() {}
8+
namespace foo {
9+
export var v = 1;
10+
}
11+
export = foo;
12+
13+
==== tests/cases/compiler/file2.ts (2 errors) ====
14+
import x = require("./file1");
15+
x.b = 1;
16+
17+
// OK - './file1' is a namespace
18+
declare module "./file1" {
19+
interface A { a }
20+
~
21+
!!! error TS2665: Module augmentation cannot introduce new names in the top level scope.
22+
let b: number;
23+
~
24+
!!! error TS2665: Module augmentation cannot introduce new names in the top level scope.
25+
}
26+
27+
==== tests/cases/compiler/file3.ts (0 errors) ====
28+
import * as x from "./file1";
29+
import "./file2";
30+
let a: x.A;
31+
let b = x.b;

0 commit comments

Comments
 (0)