Skip to content

Support augmenting module with export as namespace #27281

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
2 commits merged into from
Oct 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@ namespace ts {
(source.flags | target.flags) & SymbolFlags.Assignment) {
Debug.assert(source !== target);
if (!(target.flags & SymbolFlags.Transient)) {
target = cloneSymbol(target);
target = cloneSymbol(resolveSymbol(target));
}
// Javascript static-property-assignment declarations always merge, even though they are also values
if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) {
Expand Down Expand Up @@ -873,7 +873,7 @@ namespace ts {
else if (target.flags & SymbolFlags.NamespaceModule) {
error(getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target));
}
else {
else { // error
const isEitherEnum = !!(target.flags & SymbolFlags.Enum || source.flags & SymbolFlags.Enum);
const isEitherBlockScoped = !!(target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable);
const message = isEitherEnum
Expand Down Expand Up @@ -942,7 +942,8 @@ namespace ts {

function mergeSymbolTable(target: SymbolTable, source: SymbolTable) {
source.forEach((sourceSymbol, id) => {
target.set(id, target.has(id) ? mergeSymbol(target.get(id)!, sourceSymbol) : sourceSymbol);
const targetSymbol = target.get(id);
target.set(id, targetSymbol ? mergeSymbol(targetSymbol, sourceSymbol) : sourceSymbol);
});
}

Expand Down Expand Up @@ -1536,8 +1537,7 @@ namespace ts {

// If we're in an external module, we can't reference value symbols created from UMD export declarations
if (result && isInExternalModule && (meaning & SymbolFlags.Value) === SymbolFlags.Value && !(originalLocation!.flags & NodeFlags.JSDoc)) {
const decls = result.declarations;
if (decls && decls.length === 1 && decls[0].kind === SyntaxKind.NamespaceExportDeclaration) {
if (some(result.declarations, d => isNamespaceExportDeclaration(d) || isSourceFile(d) && !!d.symbol.globalExports)) {
error(errorLocation!, Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead, unescapeLeadingUnderscores(name)); // TODO: GH#18217
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ var z = 0;

=== tests/cases/compiler/duplicateVarsAcrossFileBoundaries_4.ts ===
module P { }
>P : Symbol(P, Decl(duplicateVarsAcrossFileBoundaries_4.ts, 0, 0))
>P : Symbol(P, Decl(duplicateVarsAcrossFileBoundaries_4.ts, 0, 0), Decl(duplicateVarsAcrossFileBoundaries_5.ts, 2, 3))

import p = P;
>p : Symbol(p, Decl(duplicateVarsAcrossFileBoundaries_4.ts, 0, 12), Decl(duplicateVarsAcrossFileBoundaries_5.ts, 2, 3))
>p : Symbol(p, Decl(duplicateVarsAcrossFileBoundaries_4.ts, 0, 12))
>P : Symbol(P, Decl(duplicateVarsAcrossFileBoundaries_4.ts, 0, 0))

var q;
Expand All @@ -52,5 +52,5 @@ import q = Q;
>Q : Symbol(Q, Decl(duplicateVarsAcrossFileBoundaries_5.ts, 0, 0))

var p;
>p : Symbol(p, Decl(duplicateVarsAcrossFileBoundaries_4.ts, 0, 12), Decl(duplicateVarsAcrossFileBoundaries_5.ts, 2, 3))
>p : Symbol(P, Decl(duplicateVarsAcrossFileBoundaries_4.ts, 0, 0), Decl(duplicateVarsAcrossFileBoundaries_5.ts, 2, 3))

50 changes: 50 additions & 0 deletions tests/baselines/reference/exportAsNamespace_augment.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/a.d.ts(3,14): error TS2451: Cannot redeclare block-scoped variable 'conflict'.
/b.ts(6,22): error TS2451: Cannot redeclare block-scoped variable 'conflict'.
/b.ts(12,18): error TS2451: Cannot redeclare block-scoped variable 'conflict'.
/b.ts(15,1): error TS2686: 'a' refers to a UMD global, but the current file is a module. Consider adding an import instead.
/b.ts(15,7): error TS2686: 'a' refers to a UMD global, but the current file is a module. Consider adding an import instead.
/b.ts(15,13): error TS2686: 'a' refers to a UMD global, but the current file is a module. Consider adding an import instead.
/b.ts(15,19): error TS2686: 'a' refers to a UMD global, but the current file is a module. Consider adding an import instead.


==== /a.d.ts (1 errors) ====
export as namespace a;
export const x = 0;
export const conflict = 0;
~~~~~~~~
!!! error TS2451: Cannot redeclare block-scoped variable 'conflict'.
!!! related TS6203 /b.ts:6:22: 'conflict' was also declared here.
!!! related TS6204 /b.ts:6:22: and here.

==== /b.ts (6 errors) ====
import * as a2 from "./a";

declare global {
namespace a {
export const y = 0;
export const conflict = 0;
~~~~~~~~
!!! error TS2451: Cannot redeclare block-scoped variable 'conflict'.
!!! related TS6203 /a.d.ts:3:14: 'conflict' was also declared here.
}
}

declare module "./a" {
export const z = 0;
export const conflict = 0;
~~~~~~~~
!!! error TS2451: Cannot redeclare block-scoped variable 'conflict'.
!!! related TS6203 /a.d.ts:3:14: 'conflict' was also declared here.
}

a.x + a.y + a.z + a.conflict;
~
!!! error TS2686: 'a' refers to a UMD global, but the current file is a module. Consider adding an import instead.
~
!!! error TS2686: 'a' refers to a UMD global, but the current file is a module. Consider adding an import instead.
~
!!! error TS2686: 'a' refers to a UMD global, but the current file is a module. Consider adding an import instead.
~
!!! error TS2686: 'a' refers to a UMD global, but the current file is a module. Consider adding an import instead.
a2.x + a2.y + a2.z + a2.conflict;

32 changes: 32 additions & 0 deletions tests/baselines/reference/exportAsNamespace_augment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//// [tests/cases/compiler/exportAsNamespace_augment.ts] ////

//// [a.d.ts]
export as namespace a;
export const x = 0;
export const conflict = 0;

//// [b.ts]
import * as a2 from "./a";

declare global {
namespace a {
export const y = 0;
export const conflict = 0;
}
}

declare module "./a" {
export const z = 0;
export const conflict = 0;
}

a.x + a.y + a.z + a.conflict;
a2.x + a2.y + a2.z + a2.conflict;


//// [b.js]
"use strict";
exports.__esModule = true;
var a2 = require("./a");
a.x + a.y + a.z + a.conflict;
a2.x + a2.y + a2.z + a2.conflict;
66 changes: 66 additions & 0 deletions tests/baselines/reference/exportAsNamespace_augment.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
=== /a.d.ts ===
export as namespace a;
>a : Symbol(a, Decl(a.d.ts, 0, 0))

export const x = 0;
>x : Symbol(x, Decl(a.d.ts, 1, 12))

export const conflict = 0;
>conflict : Symbol(conflict, Decl(a.d.ts, 2, 12))

=== /b.ts ===
import * as a2 from "./a";
>a2 : Symbol(a2, Decl(b.ts, 0, 6))

declare global {
>global : Symbol(global, Decl(b.ts, 0, 26))

namespace a {
>a : Symbol(a2, Decl(a.d.ts, 0, 0), Decl(b.ts, 2, 16), Decl(b.ts, 7, 1))

export const y = 0;
>y : Symbol(y, Decl(b.ts, 4, 20))

export const conflict = 0;
>conflict : Symbol(conflict, Decl(b.ts, 5, 20))
}
}

declare module "./a" {
>"./a" : Symbol(a2, Decl(a.d.ts, 0, 0), Decl(b.ts, 2, 16), Decl(b.ts, 7, 1))

export const z = 0;
>z : Symbol(z, Decl(b.ts, 10, 16))

export const conflict = 0;
>conflict : Symbol(conflict, Decl(b.ts, 11, 16))
}

a.x + a.y + a.z + a.conflict;
>a.x : Symbol(a2.x, Decl(a.d.ts, 1, 12))
>a : Symbol(a2, Decl(a.d.ts, 0, 0), Decl(b.ts, 2, 16), Decl(b.ts, 7, 1))
>x : Symbol(a2.x, Decl(a.d.ts, 1, 12))
>a.y : Symbol(a2.y, Decl(b.ts, 4, 20))
>a : Symbol(a2, Decl(a.d.ts, 0, 0), Decl(b.ts, 2, 16), Decl(b.ts, 7, 1))
>y : Symbol(a2.y, Decl(b.ts, 4, 20))
>a.z : Symbol(a2.z, Decl(b.ts, 10, 16))
>a : Symbol(a2, Decl(a.d.ts, 0, 0), Decl(b.ts, 2, 16), Decl(b.ts, 7, 1))
>z : Symbol(a2.z, Decl(b.ts, 10, 16))
>a.conflict : Symbol(a2.conflict, Decl(a.d.ts, 2, 12))
>a : Symbol(a2, Decl(a.d.ts, 0, 0), Decl(b.ts, 2, 16), Decl(b.ts, 7, 1))
>conflict : Symbol(a2.conflict, Decl(a.d.ts, 2, 12))

a2.x + a2.y + a2.z + a2.conflict;
>a2.x : Symbol(a2.x, Decl(a.d.ts, 1, 12))
>a2 : Symbol(a2, Decl(b.ts, 0, 6))
>x : Symbol(a2.x, Decl(a.d.ts, 1, 12))
>a2.y : Symbol(a2.y, Decl(b.ts, 4, 20))
>a2 : Symbol(a2, Decl(b.ts, 0, 6))
>y : Symbol(a2.y, Decl(b.ts, 4, 20))
>a2.z : Symbol(a2.z, Decl(b.ts, 10, 16))
>a2 : Symbol(a2, Decl(b.ts, 0, 6))
>z : Symbol(a2.z, Decl(b.ts, 10, 16))
>a2.conflict : Symbol(a2.conflict, Decl(a.d.ts, 2, 12))
>a2 : Symbol(a2, Decl(b.ts, 0, 6))
>conflict : Symbol(a2.conflict, Decl(a.d.ts, 2, 12))

78 changes: 78 additions & 0 deletions tests/baselines/reference/exportAsNamespace_augment.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
=== /a.d.ts ===
export as namespace a;
>a : typeof import("/a")

export const x = 0;
>x : 0
>0 : 0

export const conflict = 0;
>conflict : 0
>0 : 0

=== /b.ts ===
import * as a2 from "./a";
>a2 : typeof a2

declare global {
>global : typeof global

namespace a {
>a : typeof a2

export const y = 0;
>y : 0
>0 : 0

export const conflict = 0;
>conflict : 0
>0 : 0
}
}

declare module "./a" {
>"./a" : typeof a2

export const z = 0;
>z : 0
>0 : 0

export const conflict = 0;
>conflict : 0
>0 : 0
}

a.x + a.y + a.z + a.conflict;
>a.x + a.y + a.z + a.conflict : number
>a.x + a.y + a.z : number
>a.x + a.y : number
>a.x : 0
>a : typeof a2
>x : 0
>a.y : 0
>a : typeof a2
>y : 0
>a.z : 0
>a : typeof a2
>z : 0
>a.conflict : 0
>a : typeof a2
>conflict : 0

a2.x + a2.y + a2.z + a2.conflict;
>a2.x + a2.y + a2.z + a2.conflict : number
>a2.x + a2.y + a2.z : number
>a2.x + a2.y : number
>a2.x : 0
>a2 : typeof a2
>x : 0
>a2.y : 0
>a2 : typeof a2
>y : 0
>a2.z : 0
>a2 : typeof a2
>z : 0
>a2.conflict : 0
>a2 : typeof a2
>conflict : 0

22 changes: 22 additions & 0 deletions tests/cases/compiler/exportAsNamespace_augment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// @Filename: /a.d.ts
export as namespace a;
export const x = 0;
export const conflict = 0;

// @Filename: /b.ts
import * as a2 from "./a";

declare global {
namespace a {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced we need this ability. Does this PR need to exist for the declare module "./a" version to work?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, if you have:
a.d.ts

export const x = 0;
export as namespace a;

b.ts

export {};
declare module "./a" {
    export const y = 0;
}

c.ts

a.y;

It will fail in c.ts. But there is no failure for import { y } from "./a"; -- the module got augmented but the namespace didn't.

export const y = 0;
export const conflict = 0;
}
}

declare module "./a" {
export const z = 0;
export const conflict = 0;
}

a.x + a.y + a.z + a.conflict;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how can we access a in a module?

Copy link
Contributor

@saschanaz saschanaz Oct 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a is a global namespace (because of declare global at line 9-14), so IMO no issue.

a2.x + a2.y + a2.z + a2.conflict;