Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9498978

Browse filesBrowse files
authored
In JS declaration emit, move imports painted in nested contexts to the root private context (microsoft#39818)
* In JS declaration emit, move imports painted in nested contexts to the root private context * Add test for nathan
1 parent 1b97d03 commit 9498978
Copy full SHA for 9498978

10 files changed

+665-7Lines changed: 665 additions & 7 deletions
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/checker.ts‎

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+16-7Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5899,7 +5899,7 @@ namespace ts {
58995899
const enclosingDeclaration = context.enclosingDeclaration!;
59005900
let results: Statement[] = [];
59015901
const visitedSymbols = new Set<number>();
5902-
let deferredPrivates: ESMap<SymbolId, Symbol> | undefined;
5902+
const deferredPrivatesStack: ESMap<SymbolId, Symbol>[] = [];
59035903
const oldcontext = context;
59045904
context = {
59055905
...oldcontext,
@@ -6110,9 +6110,8 @@ namespace ts {
61106110
}
61116111

61126112
function visitSymbolTable(symbolTable: SymbolTable, suppressNewPrivateContext?: boolean, propertyAsAlias?: boolean) {
6113-
const oldDeferredPrivates = deferredPrivates;
61146113
if (!suppressNewPrivateContext) {
6115-
deferredPrivates = new Map();
6114+
deferredPrivatesStack.push(new Map());
61166115
}
61176116
symbolTable.forEach((symbol: Symbol) => {
61186117
serializeSymbol(symbol, /*isPrivate*/ false, !!propertyAsAlias);
@@ -6121,11 +6120,11 @@ namespace ts {
61216120
// deferredPrivates will be filled up by visiting the symbol table
61226121
// And will continue to iterate as elements are added while visited `deferredPrivates`
61236122
// (As that's how a map iterator is defined to work)
6124-
deferredPrivates!.forEach((symbol: Symbol) => {
6123+
deferredPrivatesStack[deferredPrivatesStack.length - 1].forEach((symbol: Symbol) => {
61256124
serializeSymbol(symbol, /*isPrivate*/ true, !!propertyAsAlias);
61266125
});
6126+
deferredPrivatesStack.pop();
61276127
}
6128-
deferredPrivates = oldDeferredPrivates;
61296128
}
61306129

61316130
function serializeSymbol(symbol: Symbol, isPrivate: boolean, propertyAsAlias: boolean) {
@@ -6309,9 +6308,19 @@ namespace ts {
63096308

63106309
function includePrivateSymbol(symbol: Symbol) {
63116310
if (some(symbol.declarations, isParameterDeclaration)) return;
6312-
Debug.assertIsDefined(deferredPrivates);
6311+
Debug.assertIsDefined(deferredPrivatesStack[deferredPrivatesStack.length - 1]);
63136312
getUnusedName(unescapeLeadingUnderscores(symbol.escapedName), symbol); // Call to cache unique name for symbol
6314-
deferredPrivates.set(getSymbolId(symbol), symbol);
6313+
// Blanket moving (import) aliases into the root private context should work, since imports are not valid within namespaces
6314+
// (so they must have been in the root to begin with if they were real imports) cjs `require` aliases (an upcoming feature)
6315+
// will throw a wrench in this, since those may have been nested, but we'll need to synthesize them in the outer scope
6316+
// anyway, as that's the only place the import they translate to is valid. In such a case, we might need to use a unique name
6317+
// for the moved import; which hopefully the above `getUnusedName` call should produce.
6318+
const isExternalImportAlias = !!(symbol.flags & SymbolFlags.Alias) && !some(symbol.declarations, d =>
6319+
!!findAncestor(d, isExportDeclaration) ||
6320+
isNamespaceExport(d) ||
6321+
(isImportEqualsDeclaration(d) && !isExternalModuleReference(d.moduleReference))
6322+
);
6323+
deferredPrivatesStack[isExternalImportAlias ? 0 : (deferredPrivatesStack.length - 1)].set(getSymbolId(symbol), symbol);
63156324
}
63166325

63176326
function isExportingScope(enclosingDeclaration: Node) {
Collapse file
+99Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//// [tests/cases/conformance/jsdoc/declarations/jsDeclarationsImportAliasExposedWithinNamespace.ts] ////
2+
3+
//// [file.js]
4+
/**
5+
* @namespace myTypes
6+
* @global
7+
* @type {Object<string,*>}
8+
*/
9+
const myTypes = {
10+
// SOME PROPS HERE
11+
};
12+
13+
/** @typedef {string|RegExp|Array<string|RegExp>} myTypes.typeA */
14+
15+
/**
16+
* @typedef myTypes.typeB
17+
* @property {myTypes.typeA} prop1 - Prop 1.
18+
* @property {string} prop2 - Prop 2.
19+
*/
20+
21+
/** @typedef {myTypes.typeB|Function} myTypes.typeC */
22+
23+
export {myTypes};
24+
//// [file2.js]
25+
import {myTypes} from './file.js';
26+
27+
/**
28+
* @namespace testFnTypes
29+
* @global
30+
* @type {Object<string,*>}
31+
*/
32+
const testFnTypes = {
33+
// SOME PROPS HERE
34+
};
35+
36+
/** @typedef {boolean|myTypes.typeC} testFnTypes.input */
37+
38+
/**
39+
* @function testFn
40+
* @description A test function.
41+
* @param {testFnTypes.input} input - Input.
42+
* @returns {number|null} Result.
43+
*/
44+
function testFn(input) {
45+
if (typeof input === 'number') {
46+
return 2 * input;
47+
} else {
48+
return null;
49+
}
50+
}
51+
52+
export {testFn, testFnTypes};
53+
54+
55+
56+
//// [file.d.ts]
57+
/**
58+
* @namespace myTypes
59+
* @global
60+
* @type {Object<string,*>}
61+
*/
62+
export const myTypes: {
63+
[x: string]: any;
64+
};
65+
export namespace myTypes {
66+
type typeA = string | RegExp | (string | RegExp)[];
67+
type typeB = {
68+
/**
69+
* - Prop 1.
70+
*/
71+
prop1: string | RegExp | (string | RegExp)[];
72+
/**
73+
* - Prop 2.
74+
*/
75+
prop2: string;
76+
};
77+
type typeC = Function | typeB;
78+
}
79+
//// [file2.d.ts]
80+
/** @typedef {boolean|myTypes.typeC} testFnTypes.input */
81+
/**
82+
* @function testFn
83+
* @description A test function.
84+
* @param {testFnTypes.input} input - Input.
85+
* @returns {number|null} Result.
86+
*/
87+
export function testFn(input: boolean | Function | myTypes.typeB): number | null;
88+
/**
89+
* @namespace testFnTypes
90+
* @global
91+
* @type {Object<string,*>}
92+
*/
93+
export const testFnTypes: {
94+
[x: string]: any;
95+
};
96+
export namespace testFnTypes {
97+
type input = boolean | Function | myTypes.typeB;
98+
}
99+
import { myTypes } from "./file.js";
Collapse file
+67Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
=== tests/cases/conformance/jsdoc/declarations/file.js ===
2+
/**
3+
* @namespace myTypes
4+
* @global
5+
* @type {Object<string,*>}
6+
*/
7+
const myTypes = {
8+
>myTypes : Symbol(myTypes, Decl(file.js, 5, 5), Decl(file.js, 9, 50), Decl(file.js, 12, 12), Decl(file.js, 17, 38))
9+
10+
// SOME PROPS HERE
11+
};
12+
13+
/** @typedef {string|RegExp|Array<string|RegExp>} myTypes.typeA */
14+
15+
/**
16+
* @typedef myTypes.typeB
17+
* @property {myTypes.typeA} prop1 - Prop 1.
18+
* @property {string} prop2 - Prop 2.
19+
*/
20+
21+
/** @typedef {myTypes.typeB|Function} myTypes.typeC */
22+
23+
export {myTypes};
24+
>myTypes : Symbol(myTypes, Decl(file.js, 19, 8))
25+
26+
=== tests/cases/conformance/jsdoc/declarations/file2.js ===
27+
import {myTypes} from './file.js';
28+
>myTypes : Symbol(myTypes, Decl(file2.js, 0, 8))
29+
30+
/**
31+
* @namespace testFnTypes
32+
* @global
33+
* @type {Object<string,*>}
34+
*/
35+
const testFnTypes = {
36+
>testFnTypes : Symbol(testFnTypes, Decl(file2.js, 7, 5), Decl(file2.js, 11, 37))
37+
38+
// SOME PROPS HERE
39+
};
40+
41+
/** @typedef {boolean|myTypes.typeC} testFnTypes.input */
42+
43+
/**
44+
* @function testFn
45+
* @description A test function.
46+
* @param {testFnTypes.input} input - Input.
47+
* @returns {number|null} Result.
48+
*/
49+
function testFn(input) {
50+
>testFn : Symbol(testFn, Decl(file2.js, 9, 2))
51+
>input : Symbol(input, Decl(file2.js, 19, 16))
52+
53+
if (typeof input === 'number') {
54+
>input : Symbol(input, Decl(file2.js, 19, 16))
55+
56+
return 2 * input;
57+
>input : Symbol(input, Decl(file2.js, 19, 16))
58+
59+
} else {
60+
return null;
61+
}
62+
}
63+
64+
export {testFn, testFnTypes};
65+
>testFn : Symbol(testFn, Decl(file2.js, 27, 8))
66+
>testFnTypes : Symbol(testFnTypes, Decl(file2.js, 27, 15))
67+
Collapse file
+75Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
=== tests/cases/conformance/jsdoc/declarations/file.js ===
2+
/**
3+
* @namespace myTypes
4+
* @global
5+
* @type {Object<string,*>}
6+
*/
7+
const myTypes = {
8+
>myTypes : { [x: string]: any; }
9+
>{ // SOME PROPS HERE} : {}
10+
11+
// SOME PROPS HERE
12+
};
13+
14+
/** @typedef {string|RegExp|Array<string|RegExp>} myTypes.typeA */
15+
16+
/**
17+
* @typedef myTypes.typeB
18+
* @property {myTypes.typeA} prop1 - Prop 1.
19+
* @property {string} prop2 - Prop 2.
20+
*/
21+
22+
/** @typedef {myTypes.typeB|Function} myTypes.typeC */
23+
24+
export {myTypes};
25+
>myTypes : { [x: string]: any; }
26+
27+
=== tests/cases/conformance/jsdoc/declarations/file2.js ===
28+
import {myTypes} from './file.js';
29+
>myTypes : { [x: string]: any; }
30+
31+
/**
32+
* @namespace testFnTypes
33+
* @global
34+
* @type {Object<string,*>}
35+
*/
36+
const testFnTypes = {
37+
>testFnTypes : { [x: string]: any; }
38+
>{ // SOME PROPS HERE} : {}
39+
40+
// SOME PROPS HERE
41+
};
42+
43+
/** @typedef {boolean|myTypes.typeC} testFnTypes.input */
44+
45+
/**
46+
* @function testFn
47+
* @description A test function.
48+
* @param {testFnTypes.input} input - Input.
49+
* @returns {number|null} Result.
50+
*/
51+
function testFn(input) {
52+
>testFn : (input: testFnTypes.input) => number | null
53+
>input : boolean | Function | myTypes.typeB
54+
55+
if (typeof input === 'number') {
56+
>typeof input === 'number' : boolean
57+
>typeof input : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
58+
>input : boolean | Function | myTypes.typeB
59+
>'number' : "number"
60+
61+
return 2 * input;
62+
>2 * input : number
63+
>2 : 2
64+
>input : never
65+
66+
} else {
67+
return null;
68+
>null : null
69+
}
70+
}
71+
72+
export {testFn, testFnTypes};
73+
>testFn : (input: boolean | Function | myTypes.typeB) => number
74+
>testFnTypes : { [x: string]: any; }
75+
Collapse file
+55Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
tests/cases/conformance/jsdoc/declarations/file2.js(12,31): error TS2694: Namespace 'myTypes' has no exported member 'typeC'.
2+
3+
4+
==== tests/cases/conformance/jsdoc/declarations/file2.js (1 errors) ====
5+
const {myTypes} = require('./file.js');
6+
7+
/**
8+
* @namespace testFnTypes
9+
* @global
10+
* @type {Object<string,*>}
11+
*/
12+
const testFnTypes = {
13+
// SOME PROPS HERE
14+
};
15+
16+
/** @typedef {boolean|myTypes.typeC} testFnTypes.input */
17+
~~~~~
18+
!!! error TS2694: Namespace 'myTypes' has no exported member 'typeC'.
19+
20+
/**
21+
* @function testFn
22+
* @description A test function.
23+
* @param {testFnTypes.input} input - Input.
24+
* @returns {number|null} Result.
25+
*/
26+
function testFn(input) {
27+
if (typeof input === 'number') {
28+
return 2 * input;
29+
} else {
30+
return null;
31+
}
32+
}
33+
34+
module.exports = {testFn, testFnTypes};
35+
==== tests/cases/conformance/jsdoc/declarations/file.js (0 errors) ====
36+
/**
37+
* @namespace myTypes
38+
* @global
39+
* @type {Object<string,*>}
40+
*/
41+
const myTypes = {
42+
// SOME PROPS HERE
43+
};
44+
45+
/** @typedef {string|RegExp|Array<string|RegExp>} myTypes.typeA */
46+
47+
/**
48+
* @typedef myTypes.typeB
49+
* @property {myTypes.typeA} prop1 - Prop 1.
50+
* @property {string} prop2 - Prop 2.
51+
*/
52+
53+
/** @typedef {myTypes.typeB|Function} myTypes.typeC */
54+
55+
exports.myTypes = myTypes;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.