Skip to content

Commit 3b3bd52

Browse files
weswighamtypescript-bot
authored andcommitted
Cherry-pick PR microsoft#34786 into release-3.7
Component commits: f6dbbfd Fix alias naming and structure bugs in js declarations 3005f18 Merge branch 'master' into fix-js-declaration-bugs 3bd49e4 Add another test case and change condition for ns merge to require signature or export content ca5f764 Fix typo in comment
1 parent 28050d5 commit 3b3bd52

11 files changed

+857
-81
lines changed

src/compiler/checker.ts

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4962,7 +4962,7 @@ namespace ts {
49624962
const oldcontext = context;
49634963
context = {
49644964
...oldcontext,
4965-
usedSymbolNames: mapMap(symbolTable, (_symbol, name) => [unescapeLeadingUnderscores(name), true]),
4965+
usedSymbolNames: createMap(),
49664966
remappedSymbolNames: createMap(),
49674967
tracker: {
49684968
...oldcontext.tracker,
@@ -4986,6 +4986,10 @@ namespace ts {
49864986
context.usedSymbolNames!.set(name, true);
49874987
});
49884988
}
4989+
forEachEntry(symbolTable, (symbol, name) => {
4990+
const baseName = unescapeLeadingUnderscores(name);
4991+
void getInternalSymbolName(symbol, baseName); // Called to cache values into `usedSymbolNames` and `remappedSymbolNames`
4992+
});
49894993
let addingDeclare = !bundled;
49904994
const exportEquals = symbolTable.get(InternalSymbolName.ExportEquals);
49914995
if (exportEquals && symbolTable.size > 1 && exportEquals.flags & SymbolFlags.Alias) {
@@ -5198,7 +5202,11 @@ namespace ts {
51985202
isPrivate = true;
51995203
}
52005204
const modifierFlags = (!isPrivate ? ModifierFlags.Export : 0) | (isDefault && !needsPostExportDefault ? ModifierFlags.Default : 0);
5201-
if (symbol.flags & SymbolFlags.Function) {
5205+
const isConstMergedWithNS = symbol.flags & SymbolFlags.Module &&
5206+
symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.FunctionScopedVariable | SymbolFlags.Property) &&
5207+
symbol.escapedName !== InternalSymbolName.ExportEquals;
5208+
const isConstMergedWithNSPrintableAsSignatureMerge = isConstMergedWithNS && isTypeRepresentableAsFunctionNamespaceMerge(getTypeOfSymbol(symbol), symbol);
5209+
if (symbol.flags & SymbolFlags.Function || isConstMergedWithNSPrintableAsSignatureMerge) {
52025210
serializeAsFunctionNamespaceMerge(getTypeOfSymbol(symbol), symbol, getInternalSymbolName(symbol, symbolName), modifierFlags);
52035211
}
52045212
if (symbol.flags & SymbolFlags.TypeAlias) {
@@ -5209,7 +5217,8 @@ namespace ts {
52095217
if (symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.FunctionScopedVariable | SymbolFlags.Property)
52105218
&& symbol.escapedName !== InternalSymbolName.ExportEquals
52115219
&& !(symbol.flags & SymbolFlags.Prototype)
5212-
&& !(symbol.flags & SymbolFlags.Class)) {
5220+
&& !(symbol.flags & SymbolFlags.Class)
5221+
&& !isConstMergedWithNSPrintableAsSignatureMerge) {
52135222
serializeVariableOrProperty(symbol, symbolName, isPrivate, needsPostExportDefault, propertyAsAlias, modifierFlags);
52145223
}
52155224
if (symbol.flags & SymbolFlags.Enum) {
@@ -5226,7 +5235,7 @@ namespace ts {
52265235
serializeAsClass(symbol, getInternalSymbolName(symbol, symbolName), modifierFlags);
52275236
}
52285237
}
5229-
if (symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule)) {
5238+
if ((symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule) && (!isConstMergedWithNS || isTypeOnlyNamespace(symbol))) || isConstMergedWithNSPrintableAsSignatureMerge) {
52305239
serializeModule(symbol, symbolName, modifierFlags);
52315240
}
52325241
if (symbol.flags & SymbolFlags.Interface) {
@@ -5253,7 +5262,9 @@ namespace ts {
52535262
}
52545263

52555264
function includePrivateSymbol(symbol: Symbol) {
5265+
if (some(symbol.declarations, isParameterDeclaration)) return;
52565266
Debug.assertDefined(deferredPrivates);
5267+
getUnusedName(unescapeLeadingUnderscores(symbol.escapedName), symbol); // Call to cache unique name for symbol
52575268
deferredPrivates!.set("" + getSymbolId(symbol), symbol);
52585269
}
52595270

@@ -5327,8 +5338,16 @@ namespace ts {
53275338
), modifierFlags);
53285339
}
53295340

5341+
function getNamespaceMembersForSerialization(symbol: Symbol) {
5342+
return !symbol.exports ? [] : filter(arrayFrom((symbol.exports).values()), p => !((p.flags & SymbolFlags.Prototype) || (p.escapedName === "prototype")));
5343+
}
5344+
5345+
function isTypeOnlyNamespace(symbol: Symbol) {
5346+
return every(getNamespaceMembersForSerialization(symbol), m => !(resolveSymbol(m).flags & SymbolFlags.Value));
5347+
}
5348+
53305349
function serializeModule(symbol: Symbol, symbolName: string, modifierFlags: ModifierFlags) {
5331-
const members = !symbol.exports ? [] : filter(arrayFrom((symbol.exports).values()), p => !((p.flags & SymbolFlags.Prototype) || (p.escapedName === "prototype")));
5350+
const members = getNamespaceMembersForSerialization(symbol);
53325351
// Split NS members up by declaration - members whose parent symbol is the ns symbol vs those whose is not (but were added in later via merging)
53335352
const locationMap = arrayToMultiMap(members, m => m.parent && m.parent === symbol ? "real" : "merged");
53345353
const realMembers = locationMap.get("real") || emptyArray;
@@ -5338,18 +5357,21 @@ namespace ts {
53385357
// so we don't even have placeholders to fill in.
53395358
if (length(realMembers)) {
53405359
const localName = getInternalSymbolName(symbol, symbolName);
5341-
serializeAsNamespaceDeclaration(realMembers, localName, modifierFlags, !!(symbol.flags & SymbolFlags.Function));
5360+
serializeAsNamespaceDeclaration(realMembers, localName, modifierFlags, !!(symbol.flags & (SymbolFlags.Function | SymbolFlags.Assignment)));
53425361
}
53435362
if (length(mergedMembers)) {
53445363
const localName = getInternalSymbolName(symbol, symbolName);
5345-
forEach(mergedMembers, includePrivateSymbol);
53465364
const nsBody = createModuleBlock([createExportDeclaration(
53475365
/*decorators*/ undefined,
53485366
/*modifiers*/ undefined,
53495367
createNamedExports(map(filter(mergedMembers, n => n.escapedName !== InternalSymbolName.ExportEquals), s => {
53505368
const name = unescapeLeadingUnderscores(s.escapedName);
53515369
const localName = getInternalSymbolName(s, name);
5352-
return createExportSpecifier(name === localName ? undefined : localName, name);
5370+
const aliasDecl = s.declarations && getDeclarationOfAliasSymbol(s);
5371+
const target = aliasDecl && getTargetOfAliasDeclaration(aliasDecl, /*dontRecursivelyResolve*/ true);
5372+
includePrivateSymbol(target || s);
5373+
const targetName = target ? getInternalSymbolName(target, unescapeLeadingUnderscores(target.escapedName)) : localName;
5374+
return createExportSpecifier(name === targetName ? undefined : targetName, name);
53535375
}))
53545376
)]);
53555377
addResult(createModuleDeclaration(
@@ -5624,6 +5646,7 @@ namespace ts {
56245646
serializeMaybeAliasAssignment(symbol);
56255647
break;
56265648
case SyntaxKind.BinaryExpression:
5649+
case SyntaxKind.PropertyAccessExpression:
56275650
// Could be best encoded as though an export specifier or as though an export assignment
56285651
// If name is default or export=, do an export assignment
56295652
// Otherwise do an export specifier
@@ -5634,10 +5657,6 @@ namespace ts {
56345657
serializeExportSpecifier(localName, targetName);
56355658
}
56365659
break;
5637-
case SyntaxKind.PropertyAccessExpression:
5638-
// A PAE alias is _always_ going to exist as an append to a top-level export, where our top level
5639-
// handling should always be sufficient to encode the export action itself
5640-
break;
56415660
default:
56425661
return Debug.failBadSyntaxKind(node, "Unhandled alias declaration kind in symbol serializer!");
56435662
}
@@ -5666,7 +5685,8 @@ namespace ts {
56665685
const aliasDecl = symbol.declarations && getDeclarationOfAliasSymbol(symbol);
56675686
// serialize what the alias points to, preserve the declaration's initializer
56685687
const target = aliasDecl && getTargetOfAliasDeclaration(aliasDecl, /*dontRecursivelyResolve*/ true);
5669-
if (target) {
5688+
// If the target resolves and resolves to a thing defined in this file, emit as an alias, otherwise emit as a const
5689+
if (target && length(target.declarations) && some(target.declarations, d => getSourceFileOfNode(d) === getSourceFileOfNode(enclosingDeclaration))) {
56705690
// In case `target` refers to a namespace member, look at the declaration and serialize the leftmost symbol in it
56715691
// eg, `namespace A { export class B {} }; exports = A.B;`
56725692
// Technically, this is all that's required in the case where the assignment is an entity name expression
@@ -5752,6 +5772,7 @@ namespace ts {
57525772
return getObjectFlags(typeToSerialize) & (ObjectFlags.Anonymous | ObjectFlags.Mapped) &&
57535773
!getIndexInfoOfType(typeToSerialize, IndexKind.String) &&
57545774
!getIndexInfoOfType(typeToSerialize, IndexKind.Number) &&
5775+
!!(length(getPropertiesOfType(typeToSerialize)) || length(getSignaturesOfType(typeToSerialize, SignatureKind.Call))) &&
57555776
!length(getSignaturesOfType(typeToSerialize, SignatureKind.Construct)) && // TODO: could probably serialize as function + ns + class, now that that's OK
57565777
!getDeclarationWithTypeAnnotation(hostSymbol) &&
57575778
!(typeToSerialize.symbol && some(typeToSerialize.symbol.declarations, d => getSourceFileOfNode(d) !== ctxSrc)) &&
@@ -6106,11 +6127,8 @@ namespace ts {
61066127
return context.remappedSymbolNames!.get("" + getSymbolId(symbol))!;
61076128
}
61086129
}
6109-
if (input === InternalSymbolName.Default) {
6110-
input = "_default";
6111-
}
6112-
else if (input === InternalSymbolName.ExportEquals) {
6113-
input = "_exports";
6130+
if (symbol) {
6131+
input = getNameCandidateWorker(symbol, input);
61146132
}
61156133
let i = 0;
61166134
const original = input;
@@ -6125,17 +6143,29 @@ namespace ts {
61256143
return input;
61266144
}
61276145

6128-
function getInternalSymbolName(symbol: Symbol, localName: string) {
6129-
if (context.remappedSymbolNames!.has("" + getSymbolId(symbol))) {
6130-
return context.remappedSymbolNames!.get("" + getSymbolId(symbol))!;
6131-
}
6146+
function getNameCandidateWorker(symbol: Symbol, localName: string) {
61326147
if (localName === InternalSymbolName.Default || localName === InternalSymbolName.Class || localName === InternalSymbolName.Function) {
61336148
const flags = context.flags;
61346149
context.flags |= NodeBuilderFlags.InInitialEntityName;
61356150
const nameCandidate = getNameOfSymbolAsWritten(symbol, context);
61366151
context.flags = flags;
6137-
localName = isIdentifierText(nameCandidate, languageVersion) && !isStringANonContextualKeyword(nameCandidate) ? nameCandidate : getUnusedName(`_default`, symbol);
6152+
localName = nameCandidate.length > 0 && isSingleOrDoubleQuote(nameCandidate.charCodeAt(0)) ? stripQuotes(nameCandidate) : nameCandidate;
6153+
}
6154+
if (localName === InternalSymbolName.Default) {
6155+
localName = "_default";
6156+
}
6157+
else if (localName === InternalSymbolName.ExportEquals) {
6158+
localName = "_exports";
6159+
}
6160+
localName = isIdentifierText(localName, languageVersion) && !isStringANonContextualKeyword(localName) ? localName : "_" + localName.replace(/[^a-zA-Z0-9]/g, "_");
6161+
return localName;
6162+
}
6163+
6164+
function getInternalSymbolName(symbol: Symbol, localName: string) {
6165+
if (context.remappedSymbolNames!.has("" + getSymbolId(symbol))) {
6166+
return context.remappedSymbolNames!.get("" + getSymbolId(symbol))!;
61386167
}
6168+
localName = getNameCandidateWorker(symbol, localName);
61396169
// The result of this is going to be used as the symbol's name - lock it in, so `getUnusedName` will also pick it up
61406170
context.remappedSymbolNames!.set("" + getSymbolId(symbol), localName);
61416171
return localName;
Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//// [index.js]
2-
// TODO: Fixup
32
class A {
43
member = new Q();
54
}
@@ -15,7 +14,6 @@ module.exports.Another = Q;
1514

1615

1716
//// [index.js]
18-
// TODO: Fixup
1917
var A = /** @class */ (function () {
2018
function A() {
2119
this.member = new Q();
@@ -43,44 +41,11 @@ declare class Q {
4341
x: A;
4442
}
4543
declare namespace Q {
46-
export { Another };
44+
export { Q_1 as Another };
4745
}
4846
declare class A {
4947
member: Q;
5048
}
51-
declare var Another: typeof Q;
52-
declare class Q {
49+
declare class Q_1 {
5350
x: number;
5451
}
55-
56-
57-
//// [DtsFileErrors]
58-
59-
60-
out/index.d.ts(2,15): error TS2300: Duplicate identifier 'Q'.
61-
out/index.d.ts(5,19): error TS2300: Duplicate identifier 'Q'.
62-
out/index.d.ts(12,15): error TS2300: Duplicate identifier 'Q'.
63-
64-
65-
==== ./out/index.d.ts (3 errors) ====
66-
export = Q;
67-
declare class Q {
68-
~
69-
!!! error TS2300: Duplicate identifier 'Q'.
70-
x: A;
71-
}
72-
declare namespace Q {
73-
~
74-
!!! error TS2300: Duplicate identifier 'Q'.
75-
export { Another };
76-
}
77-
declare class A {
78-
member: Q;
79-
}
80-
declare var Another: typeof Q;
81-
declare class Q {
82-
~
83-
!!! error TS2300: Duplicate identifier 'Q'.
84-
x: number;
85-
}
86-
Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,36 @@
11
=== tests/cases/conformance/jsdoc/declarations/index.js ===
2-
// TODO: Fixup
32
class A {
43
>A : Symbol(A, Decl(index.js, 0, 0))
54

65
member = new Q();
7-
>member : Symbol(A.member, Decl(index.js, 1, 9))
8-
>Q : Symbol(Q, Decl(index.js, 3, 1))
6+
>member : Symbol(A.member, Decl(index.js, 0, 9))
7+
>Q : Symbol(Q, Decl(index.js, 2, 1))
98
}
109
class Q {
11-
>Q : Symbol(Q, Decl(index.js, 3, 1))
10+
>Q : Symbol(Q, Decl(index.js, 2, 1))
1211

1312
x = 42;
14-
>x : Symbol(Q.x, Decl(index.js, 4, 9))
13+
>x : Symbol(Q.x, Decl(index.js, 3, 9))
1514
}
1615
module.exports = class Q {
1716
>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/index", Decl(index.js, 0, 0))
18-
>module : Symbol(export=, Decl(index.js, 6, 1))
19-
>exports : Symbol(export=, Decl(index.js, 6, 1))
20-
>Q : Symbol(Q, Decl(index.js, 7, 16))
17+
>module : Symbol(export=, Decl(index.js, 5, 1))
18+
>exports : Symbol(export=, Decl(index.js, 5, 1))
19+
>Q : Symbol(Q, Decl(index.js, 6, 16))
2120

2221
constructor() {
2322
this.x = new A();
24-
>this.x : Symbol(Q.x, Decl(index.js, 8, 19))
25-
>this : Symbol(Q, Decl(index.js, 7, 16))
26-
>x : Symbol(Q.x, Decl(index.js, 8, 19))
23+
>this.x : Symbol(Q.x, Decl(index.js, 7, 19))
24+
>this : Symbol(Q, Decl(index.js, 6, 16))
25+
>x : Symbol(Q.x, Decl(index.js, 7, 19))
2726
>A : Symbol(A, Decl(index.js, 0, 0))
2827
}
2928
}
3029
module.exports.Another = Q;
3130
>module.exports.Another : Symbol(Another)
32-
>module.exports : Symbol(Another, Decl(index.js, 11, 1))
33-
>module : Symbol(module, Decl(index.js, 6, 1))
31+
>module.exports : Symbol(Another, Decl(index.js, 10, 1))
32+
>module : Symbol(module, Decl(index.js, 5, 1))
3433
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/index", Decl(index.js, 0, 0))
35-
>Another : Symbol(Another, Decl(index.js, 11, 1))
36-
>Q : Symbol(Q, Decl(index.js, 3, 1))
34+
>Another : Symbol(Another, Decl(index.js, 10, 1))
35+
>Q : Symbol(Q, Decl(index.js, 2, 1))
3736

tests/baselines/reference/jsDeclarationsExportAssignedClassExpressionShadowing.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
=== tests/cases/conformance/jsdoc/declarations/index.js ===
2-
// TODO: Fixup
32
class A {
43
>A : A
54

tests/baselines/reference/jsDeclarationsFunctionsCjs.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,7 @@ export namespace b {
121121
}
122122
export function c(): void;
123123
export namespace c {
124-
class Cls {
125-
}
124+
export { Cls };
126125
}
127126
export function d(a: number, b: number): string;
128127
export function e<T, U>(a: T, b: U): T & U;
@@ -159,3 +158,6 @@ export function i(): void;
159158
export function ii(): void;
160159
export function jj(): void;
161160
export function j(): void;
161+
declare class Cls {
162+
}
163+
export {};

0 commit comments

Comments
 (0)