Skip to content

Commit c6f7c82

Browse files
committed
Fix values and types merging in JS module exports
1 parent 795a5c8 commit c6f7c82

17 files changed

+198
-34
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7646,11 +7646,16 @@ namespace ts {
76467646
resolvedSymbol.exports = createSymbolTable();
76477647
}
76487648
(resolvedSymbol || symbol).exports!.forEach((s, name) => {
7649-
if (members.has(name)) {
7650-
const exportedMember = exportedType.members.get(name)!;
7651-
const union = createSymbol(s.flags | exportedMember.flags, name);
7652-
union.type = getUnionType([getTypeOfSymbol(s), getTypeOfSymbol(exportedMember)]);
7653-
members.set(name, union);
7649+
const exportedMember = members.get(name)!;
7650+
if (exportedMember && exportedMember !== s) {
7651+
if (s.flags & SymbolFlags.Value) {
7652+
const union = createSymbol(s.flags | exportedMember.flags, name);
7653+
union.type = getUnionType([getTypeOfSymbol(s), getTypeOfSymbol(exportedMember)]);
7654+
members.set(name, union);
7655+
}
7656+
else {
7657+
members.set(name, mergeSymbol(s, exportedMember));
7658+
}
76547659
}
76557660
else {
76567661
members.set(name, s);

tests/baselines/reference/jsDeclarationsClassExtendsVisibility.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ module.exports = Foo;
2525
>Foo : Symbol(Foo, Decl(cls.js, 4, 2))
2626

2727
module.exports.Strings = Strings;
28-
>module.exports.Strings : Symbol(Strings)
28+
>module.exports.Strings : Symbol(Strings, Decl(cls.js, 6, 21))
2929
>module.exports : Symbol(Strings, Decl(cls.js, 6, 21))
3030
>module : Symbol(module, Decl(cls.js, 5, 24))
3131
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/cls", Decl(cls.js, 0, 0))

tests/baselines/reference/jsDeclarationsClassStatic.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ module.exports = Handler;
3434
>Handler : Symbol(Handler, Decl(source.js, 0, 0), Decl(source.js, 7, 1))
3535

3636
module.exports.Strings = Strings
37-
>module.exports.Strings : Symbol(Strings)
37+
>module.exports.Strings : Symbol(Strings, Decl(source.js, 14, 25))
3838
>module.exports : Symbol(Strings, Decl(source.js, 14, 25))
3939
>module : Symbol(module, Decl(source.js, 12, 1))
4040
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/source", Decl(source.js, 0, 0))

tests/baselines/reference/jsDeclarationsExportAssignedClassExpressionAnonymousWithSub.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module.exports = class {
1818
}
1919
}
2020
module.exports.Sub = class {
21-
>module.exports.Sub : Symbol(Sub)
21+
>module.exports.Sub : Symbol(Sub, Decl(index.js, 7, 1))
2222
>module.exports : Symbol(Sub, Decl(index.js, 7, 1))
2323
>module : Symbol(module, Decl(index.js, 0, 0), Decl(index.js, 10, 27))
2424
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/index", Decl(index.js, 0, 0))

tests/baselines/reference/jsDeclarationsExportAssignedClassExpressionShadowing.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ module.exports = class Q {
2727
}
2828
}
2929
module.exports.Another = Q;
30-
>module.exports.Another : Symbol(Another)
30+
>module.exports.Another : Symbol(Another, Decl(index.js, 10, 1))
3131
>module.exports : Symbol(Another, Decl(index.js, 10, 1))
3232
>module : Symbol(module, Decl(index.js, 5, 1))
3333
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/index", Decl(index.js, 0, 0))

tests/baselines/reference/jsDeclarationsExportSubAssignments.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ module.exports = Foo;
1919
>Foo : Symbol(Foo, Decl(cls.js, 3, 2))
2020

2121
module.exports.Strings = Strings;
22-
>module.exports.Strings : Symbol(Strings)
22+
>module.exports.Strings : Symbol(Strings, Decl(cls.js, 5, 21))
2323
>module.exports : Symbol(Strings, Decl(cls.js, 5, 21))
2424
>module : Symbol(module, Decl(cls.js, 4, 12))
2525
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/cls", Decl(cls.js, 0, 0))
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== /test.js ===
2+
class Abcde {
3+
>Abcde : Symbol(Abcde, Decl(test.js, 0, 0))
4+
5+
/** @type {string} */
6+
x;
7+
>x : Symbol(Abcde.x, Decl(test.js, 0, 13))
8+
}
9+
10+
module.exports = {
11+
>module.exports : Symbol("/test", Decl(test.js, 0, 0))
12+
>module : Symbol("/test.js", Decl(test.js, 3, 1), Decl(index.ts, 0, 31))
13+
>exports : Symbol("/test.js", Decl(test.js, 3, 1), Decl(index.ts, 0, 31))
14+
15+
Abcde
16+
>Abcde : Symbol(Abcde, Decl(test.js, 5, 18))
17+
18+
};
19+
20+
=== /index.ts ===
21+
import { Abcde } from "./test";
22+
>Abcde : Symbol(Abcde, Decl(index.ts, 0, 8))
23+
24+
declare module "./test" {
25+
>"./test" : Symbol("/test.js", Decl(test.js, 3, 1), Decl(index.ts, 0, 31))
26+
27+
interface Abcde { b: string }
28+
>Abcde : Symbol(Abcde, Decl(index.ts, 2, 25), Decl(test.js, 5, 18))
29+
>b : Symbol(Abcde.b, Decl(index.ts, 3, 19))
30+
}
31+
32+
new Abcde().x;
33+
>new Abcde().x : Symbol(Abcde.x, Decl(test.js, 0, 13))
34+
>Abcde : Symbol(Abcde, Decl(index.ts, 0, 8))
35+
>x : Symbol(Abcde.x, Decl(test.js, 0, 13))
36+
37+
// Descriptive, not prescriptive: This is ok,
38+
// because the type meaning from /test.js does
39+
// not propagate through the object literal export.
40+
const x: Abcde = { b: "" };
41+
>x : Symbol(x, Decl(index.ts, 11, 5))
42+
>Abcde : Symbol(Abcde, Decl(index.ts, 0, 8))
43+
>b : Symbol(b, Decl(index.ts, 11, 18))
44+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== /test.js ===
2+
class Abcde {
3+
>Abcde : Abcde
4+
5+
/** @type {string} */
6+
x;
7+
>x : string
8+
}
9+
10+
module.exports = {
11+
>module.exports = { Abcde} : { Abcde: typeof Abcde; }
12+
>module.exports : { Abcde: typeof Abcde; }
13+
>module : { "\"/test\"": { Abcde: typeof Abcde; }; }
14+
>exports : { Abcde: typeof Abcde; }
15+
>{ Abcde} : { Abcde: typeof Abcde; }
16+
17+
Abcde
18+
>Abcde : typeof Abcde
19+
20+
};
21+
22+
=== /index.ts ===
23+
import { Abcde } from "./test";
24+
>Abcde : typeof Abcde
25+
26+
declare module "./test" {
27+
>"./test" : { Abcde: typeof Abcde; }
28+
29+
interface Abcde { b: string }
30+
>b : string
31+
}
32+
33+
new Abcde().x;
34+
>new Abcde().x : string
35+
>new Abcde() : Abcde
36+
>Abcde : typeof Abcde
37+
>x : string
38+
39+
// Descriptive, not prescriptive: This is ok,
40+
// because the type meaning from /test.js does
41+
// not propagate through the object literal export.
42+
const x: Abcde = { b: "" };
43+
>x : Abcde
44+
>{ b: "" } : { b: string; }
45+
>b : string
46+
>"" : ""
47+

tests/baselines/reference/moduleExportAlias2.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ exports = module.exports = C
3737
>C : Symbol(C, Decl(semver.js, 2, 22))
3838

3939
exports.f = n => n + 1
40-
>exports.f : Symbol(f)
40+
>exports.f : Symbol(f, Decl(semver.js, 1, 28))
4141
>exports : Symbol(f, Decl(semver.js, 1, 28))
4242
>f : Symbol(f, Decl(semver.js, 1, 28))
4343
>n : Symbol(n, Decl(semver.js, 2, 11))

tests/baselines/reference/moduleExportAlias4.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = class C {}
1212
>C : Symbol(C, Decl(bug24024.js, 2, 16))
1313

1414
module.exports.D = class D { }
15-
>module.exports.D : Symbol(D)
15+
>module.exports.D : Symbol(D, Decl(bug24024.js, 2, 27))
1616
>module.exports : Symbol(D, Decl(bug24024.js, 2, 27))
1717
>module : Symbol(module, Decl(bug24024.js, 1, 31))
1818
>exports : Symbol("tests/cases/conformance/salsa/bug24024", Decl(bug24024.js, 0, 0))

tests/baselines/reference/moduleExportAliasImported.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== tests/cases/conformance/salsa/bug28014.js ===
22
exports.version = 1
3-
>exports.version : Symbol(version)
3+
>exports.version : Symbol(version, Decl(bug28014.js, 0, 0))
44
>exports : Symbol(version, Decl(bug28014.js, 0, 0))
55
>version : Symbol(version, Decl(bug28014.js, 0, 0))
66

tests/baselines/reference/moduleExportPropertyAssignmentDefault.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module.exports = axios // both assignments should be ok
99
>axios : Symbol(axios, Decl(axios.js, 0, 3))
1010

1111
module.exports.default = axios
12-
>module.exports.default : Symbol(default)
12+
>module.exports.default : Symbol(default, Decl(axios.js, 1, 22))
1313
>module.exports : Symbol(default, Decl(axios.js, 1, 22))
1414
>module : Symbol(module, Decl(axios.js, 0, 14))
1515
>exports : Symbol("tests/cases/conformance/salsa/axios", Decl(axios.js, 0, 0))

tests/baselines/reference/moduleExportWithExportPropertyAssignment4.symbols

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ declare function require(name: string): any;
4141
=== tests/cases/conformance/salsa/mod1.js ===
4242
/// <reference path='./requires.d.ts' />
4343
module.exports.bothBefore = 'string'
44-
>module.exports.bothBefore : Symbol(bothBefore)
44+
>module.exports.bothBefore : Symbol(bothBefore, Decl(mod1.js, 2, 16), Decl(mod1.js, 0, 0))
4545
>module.exports : Symbol(bothBefore, Decl(mod1.js, 2, 16), Decl(mod1.js, 0, 0))
4646
>module : Symbol(module, Decl(mod1.js, 0, 0))
4747
>exports : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0))
@@ -75,14 +75,14 @@ function A() {
7575
>p : Symbol(A.p, Decl(mod1.js, 6, 14))
7676
}
7777
module.exports.bothAfter = 'string'
78-
>module.exports.bothAfter : Symbol(bothAfter)
78+
>module.exports.bothAfter : Symbol(bothAfter, Decl(mod1.js, 3, 16), Decl(mod1.js, 8, 1))
7979
>module.exports : Symbol(bothAfter, Decl(mod1.js, 3, 16), Decl(mod1.js, 8, 1))
8080
>module : Symbol(module, Decl(mod1.js, 0, 0))
8181
>exports : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0))
8282
>bothAfter : Symbol(bothAfter, Decl(mod1.js, 3, 16), Decl(mod1.js, 8, 1))
8383

8484
module.exports.justProperty = 'string'
85-
>module.exports.justProperty : Symbol(justProperty)
85+
>module.exports.justProperty : Symbol(justProperty, Decl(mod1.js, 9, 35))
8686
>module.exports : Symbol(justProperty, Decl(mod1.js, 9, 35))
8787
>module : Symbol(module, Decl(mod1.js, 0, 0))
8888
>exports : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0))

tests/baselines/reference/typedefCrossModule2.types

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
=== tests/cases/conformance/jsdoc/use.js ===
22
var mod = require('./mod1.js');
3-
>mod : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }
4-
>require('./mod1.js') : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }
3+
>mod : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }
4+
>require('./mod1.js') : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }
55
>require : any
66
>'./mod1.js' : "./mod1.js"
77

@@ -17,7 +17,7 @@ var bbb = new mod.Baz();
1717
>bbb : any
1818
>new mod.Baz() : any
1919
>mod.Baz : any
20-
>mod : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }
20+
>mod : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }
2121
>Baz : any
2222

2323
=== tests/cases/conformance/jsdoc/mod1.js ===
@@ -31,16 +31,16 @@ class Foo { } // should error
3131
exports.Bar = class { }
3232
>exports.Bar = class { } : typeof Bar
3333
>exports.Bar : typeof Bar
34-
>exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }
34+
>exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }
3535
>Bar : typeof Bar
3636
>class { } : typeof Bar
3737

3838
/** @typedef {number} Baz */
3939
module.exports = {
40-
>module.exports = { Baz: class { }} : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }
41-
>module.exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }
42-
>module : { "\"tests/cases/conformance/jsdoc/mod1\"": { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }; }
43-
>exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }
40+
>module.exports = { Baz: class { }} : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }
41+
>module.exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }
42+
>module : { "\"tests/cases/conformance/jsdoc/mod1\"": { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }; }
43+
>exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }
4444
>{ Baz: class { }} : { Baz: typeof Baz; }
4545

4646
Baz: class { }
@@ -59,16 +59,16 @@ var Qux = 2;
5959
exports.Quid = 2;
6060
>exports.Quid = 2 : 2
6161
>exports.Quid : number
62-
>exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }
62+
>exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }
6363
>Quid : number
6464
>2 : 2
6565

6666
/** @typedef {number} Quack */
6767
module.exports = {
68-
>module.exports = { Quack: 2} : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }
69-
>module.exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }
70-
>module : { "\"tests/cases/conformance/jsdoc/mod1\"": { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }; }
71-
>exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }
68+
>module.exports = { Quack: 2} : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }
69+
>module.exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }
70+
>module : { "\"tests/cases/conformance/jsdoc/mod1\"": { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }; }
71+
>exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }
7272
>{ Quack: 2} : { Quack: number; }
7373

7474
Quack: 2

tests/baselines/reference/typedefCrossModule4.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ class Bar { }
44
>Bar : Bar
55

66
module.exports = { Foo: Bar };
7-
>module.exports = { Foo: Bar } : { Foo: any; }
8-
>module.exports : { Foo: any; }
9-
>module : { "\"tests/cases/conformance/jsdoc/mod3\"": { Foo: any; }; }
10-
>exports : { Foo: any; }
7+
>module.exports = { Foo: Bar } : { Foo: typeof Bar; }
8+
>module.exports : { Foo: typeof Bar; }
9+
>module : { "\"tests/cases/conformance/jsdoc/mod3\"": { Foo: typeof Bar; }; }
10+
>exports : { Foo: typeof Bar; }
1111
>{ Foo: Bar } : { Foo: typeof Bar; }
1212
>Foo : typeof Bar
1313
>Bar : typeof Bar
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @noEmit: true
4+
5+
// @Filename: /test.js
6+
class Abcde {
7+
/** @type {string} */
8+
x;
9+
}
10+
11+
module.exports = {
12+
Abcde
13+
};
14+
15+
// @Filename: /index.ts
16+
import { Abcde } from "./test";
17+
18+
declare module "./test" {
19+
interface Abcde { b: string }
20+
}
21+
22+
new Abcde().x;
23+
24+
// Descriptive, not prescriptive: This is ok,
25+
// because the type meaning from /test.js does
26+
// not propagate through the object literal export.
27+
const x: Abcde = { b: "" };
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// #37833
2+
3+
/// <reference path="fourslash.ts" />
4+
5+
// @allowJs: true
6+
// @checkJs: true
7+
// @noEmit: true
8+
9+
// @Filename: /test.js
10+
////class Abcde {
11+
//// x
12+
////}
13+
////
14+
////module.exports = {
15+
//// Abcde
16+
////};
17+
18+
// @Filename: /index.ts
19+
////export {};
20+
////declare module "./test" {
21+
//// interface Abcde { b: string }
22+
////}
23+
////
24+
////Abcde/**/
25+
26+
verify.applyCodeActionFromCompletion("", {
27+
name: "Abcde",
28+
source: "/test",
29+
description: `Import 'Abcde' from module "./test"`,
30+
newFileContent: `import { Abcde } from "./test";
31+
32+
export {};
33+
declare module "./test" {
34+
interface Abcde { b: string }
35+
}
36+
37+
Abcde`,
38+
preferences: {
39+
includeCompletionsForModuleExports: true
40+
}
41+
});

0 commit comments

Comments
 (0)