From 43d7b9a2fad31fa4d038cc02c3f4d1d75e38c4f6 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 26 Apr 2018 14:00:10 -0700 Subject: [PATCH 1/5] Always export typedefs This actually just required deleting a check in declareModuleMembers and checking for external AND commonjs modules in a couple of places. However, while experimenting with this feature, I discovered that even previously-exported typedefs would only be exported if they came after a commonjs export node. So I added a commonjs check to the pass in the parser. It will not catch nested module.exports, but it will catch top-level assignments. The new test tests both changes. --- src/compiler/binder.ts | 7 +- src/compiler/parser.ts | 13 +++ src/harness/harness.ts | 2 +- .../reference/jsdocTypedefNoCrash.symbols | 2 +- .../reference/typedefCrossModule.symbols | 70 +++++++++++++++ .../reference/typedefCrossModule.types | 88 +++++++++++++++++++ .../conformance/jsdoc/typedefCrossModule.ts | 44 ++++++++++ 7 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/typedefCrossModule.symbols create mode 100644 tests/baselines/reference/typedefCrossModule.types create mode 100644 tests/cases/conformance/jsdoc/typedefCrossModule.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 2557e3b707827..2e94c84596717 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -450,8 +450,7 @@ namespace ts { // and this case is specially handled. Module augmentations should only be merged with original module definition // and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed. if (node.kind === SyntaxKind.JSDocTypedefTag) Debug.assert(isInJavaScriptFile(node)); // We shouldn't add symbols for JSDoc nodes if not in a JS file. - const isJSDocTypedefInJSDocNamespace = isJSDocTypedefTag(node) && node.name && node.name.kind === SyntaxKind.Identifier && node.name.isInJSDocNamespace; - if ((!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) || isJSDocTypedefInJSDocNamespace) { + if ((!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) || isJSDocTypedefTag(node)) { if (hasModifier(node, ModifierFlags.Default) && !getDeclarationName(node)) { return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); // No local symbol for an unnamed default! } @@ -1727,7 +1726,7 @@ namespace ts { declareModuleMember(node, symbolFlags, symbolExcludes); break; case SyntaxKind.SourceFile: - if (isExternalModule(container)) { + if (isExternalOrCommonJsModule(container)) { declareModuleMember(node, symbolFlags, symbolExcludes); break; } @@ -2211,7 +2210,7 @@ namespace ts { function bindSourceFileIfExternalModule() { setExportContextFlag(file); - if (isExternalModule(file)) { + if (isExternalOrCommonJsModule(file)) { bindSourceFileAsExternalModule(); } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8547eb171147a..b67efe9dfb8c9 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6116,6 +6116,19 @@ namespace ts { || node.kind === SyntaxKind.ExportDeclaration ? node : undefined); + if (isInJavaScriptFile(sourceFile) && !sourceFile.externalModuleIndicator) { + sourceFile.commonJsModuleIndicator = forEach(sourceFile.statements, isCommonJsModuleIndicator); + } + } + + function isCommonJsModuleIndicator(statement: Statement) { + if (isExpressionStatement(statement) && isBinaryExpression(statement.expression)) { + const special = getSpecialPropertyAssignmentKind(statement.expression); + if (special === SpecialPropertyAssignmentKind.ModuleExports || + special === SpecialPropertyAssignmentKind.ExportsProperty) { + return statement.expression.left; + } + } } const enum ParsingContext { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 9fe26ed51c3ba..c955c1acb59d2 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -217,7 +217,7 @@ namespace Utils { for (const childName in node) { if (childName === "parent" || childName === "nextContainer" || childName === "modifiers" || childName === "externalModuleIndicator" || // for now ignore jsdoc comments - childName === "jsDocComment" || childName === "checkJsDirective") { + childName === "jsDocComment" || childName === "checkJsDirective" || childName === "commonJsModuleIndicator") { continue; } const child = (node)[childName]; diff --git a/tests/baselines/reference/jsdocTypedefNoCrash.symbols b/tests/baselines/reference/jsdocTypedefNoCrash.symbols index 8724c9da8d171..43ba5734073cc 100644 --- a/tests/baselines/reference/jsdocTypedefNoCrash.symbols +++ b/tests/baselines/reference/jsdocTypedefNoCrash.symbols @@ -4,5 +4,5 @@ * }} */ export const foo = 5; ->foo : Symbol(foo, Decl(export.js, 4, 12)) +>foo : Symbol(foo, Decl(export.js, 1, 3), Decl(export.js, 4, 12)) diff --git a/tests/baselines/reference/typedefCrossModule.symbols b/tests/baselines/reference/typedefCrossModule.symbols new file mode 100644 index 0000000000000..2ba91903ba3ad --- /dev/null +++ b/tests/baselines/reference/typedefCrossModule.symbols @@ -0,0 +1,70 @@ +=== tests/cases/conformance/jsdoc/commonjs.d.ts === +declare var module: { exports: any}; +>module : Symbol(module, Decl(commonjs.d.ts, 0, 11)) +>exports : Symbol(exports, Decl(commonjs.d.ts, 0, 21)) + +=== tests/cases/conformance/jsdoc/mod1.js === +/// +/** @typedef {{ type: "a", x: 1 }} A */ +/** @typedef {{ type: "b", y: 1 }} B */ +/** @typedef {A | B} Both */ +module.exports = C +>module.exports : Symbol(exports, Decl(commonjs.d.ts, 0, 21)) +>module : Symbol(export=, Decl(mod1.js, 0, 0)) +>exports : Symbol(export=, Decl(mod1.js, 0, 0)) +>C : Symbol(C, Decl(mod1.js, 4, 18)) + +function C() { +>C : Symbol(C, Decl(mod1.js, 4, 18)) + + this.p = 1 +>p : Symbol(C.p, Decl(mod1.js, 5, 14)) +} + +=== tests/cases/conformance/jsdoc/mod2.js === +/// +/** @typedef {{ type: "a", x: 1 }} A */ +/** @typedef {{ type: "b", y: 1 }} B */ +/** @typedef {A | B} Both */ + +export function C() { +>C : Symbol(C, Decl(mod2.js, 0, 0)) + + this.p = 1 +>p : Symbol(C.p, Decl(mod2.js, 5, 21)) +} + +=== tests/cases/conformance/jsdoc/mod3.js === +/// +/** @typedef {{ type: "a", x: 1 }} A */ +/** @typedef {{ type: "b", y: 1 }} B */ +/** @typedef {A | B} Both */ + +exports.C = function() { +>exports.C : Symbol(C, Decl(mod3.js, 0, 0)) +>exports : Symbol(C, Decl(mod3.js, 0, 0)) +>C : Symbol(C, Decl(mod3.js, 0, 0)) + + this.p = 1 +>p : Symbol(C.p, Decl(mod3.js, 5, 24)) +} + +=== tests/cases/conformance/jsdoc/use.js === +/** @type {import('./mod1').Both} */ +var both1 = { type: 'a', x: 1 }; +>both1 : Symbol(both1, Decl(use.js, 1, 3)) +>type : Symbol(type, Decl(use.js, 1, 13)) +>x : Symbol(x, Decl(use.js, 1, 24)) + +/** @type {import('./mod2').Both} */ +var both2 = both1; +>both2 : Symbol(both2, Decl(use.js, 3, 3)) +>both1 : Symbol(both1, Decl(use.js, 1, 3)) + +/** @type {import('./mod3').Both} */ +var both3 = both2; +>both3 : Symbol(both3, Decl(use.js, 5, 3)) +>both2 : Symbol(both2, Decl(use.js, 3, 3)) + + + diff --git a/tests/baselines/reference/typedefCrossModule.types b/tests/baselines/reference/typedefCrossModule.types new file mode 100644 index 0000000000000..665fa4989918b --- /dev/null +++ b/tests/baselines/reference/typedefCrossModule.types @@ -0,0 +1,88 @@ +=== tests/cases/conformance/jsdoc/commonjs.d.ts === +declare var module: { exports: any}; +>module : { exports: any; } +>exports : any + +=== tests/cases/conformance/jsdoc/mod1.js === +/// +/** @typedef {{ type: "a", x: 1 }} A */ +/** @typedef {{ type: "b", y: 1 }} B */ +/** @typedef {A | B} Both */ +module.exports = C +>module.exports = C : typeof C +>module.exports : any +>module : { exports: any; } +>exports : any +>C : typeof C + +function C() { +>C : typeof C + + this.p = 1 +>this.p = 1 : 1 +>this.p : any +>this : any +>p : any +>1 : 1 +} + +=== tests/cases/conformance/jsdoc/mod2.js === +/// +/** @typedef {{ type: "a", x: 1 }} A */ +/** @typedef {{ type: "b", y: 1 }} B */ +/** @typedef {A | B} Both */ + +export function C() { +>C : typeof C + + this.p = 1 +>this.p = 1 : 1 +>this.p : any +>this : any +>p : any +>1 : 1 +} + +=== tests/cases/conformance/jsdoc/mod3.js === +/// +/** @typedef {{ type: "a", x: 1 }} A */ +/** @typedef {{ type: "b", y: 1 }} B */ +/** @typedef {A | B} Both */ + +exports.C = function() { +>exports.C = function() { this.p = 1} : typeof C +>exports.C : typeof C +>exports : typeof import("tests/cases/conformance/jsdoc/mod3") +>C : typeof C +>function() { this.p = 1} : typeof C + + this.p = 1 +>this.p = 1 : 1 +>this.p : any +>this : any +>p : any +>1 : 1 +} + +=== tests/cases/conformance/jsdoc/use.js === +/** @type {import('./mod1').Both} */ +var both1 = { type: 'a', x: 1 }; +>both1 : { type: "a"; x: 1; } | { type: "b"; y: 1; } +>{ type: 'a', x: 1 } : { type: "a"; x: 1; } +>type : "a" +>'a' : "a" +>x : 1 +>1 : 1 + +/** @type {import('./mod2').Both} */ +var both2 = both1; +>both2 : { type: "a"; x: 1; } | { type: "b"; y: 1; } +>both1 : { type: "a"; x: 1; } + +/** @type {import('./mod3').Both} */ +var both3 = both2; +>both3 : { type: "a"; x: 1; } | { type: "b"; y: 1; } +>both2 : { type: "a"; x: 1; } + + + diff --git a/tests/cases/conformance/jsdoc/typedefCrossModule.ts b/tests/cases/conformance/jsdoc/typedefCrossModule.ts new file mode 100644 index 0000000000000..772a41da7bf82 --- /dev/null +++ b/tests/cases/conformance/jsdoc/typedefCrossModule.ts @@ -0,0 +1,44 @@ +// @noEmit: true +// @allowJs: true +// @checkJs: true +// @Filename: commonjs.d.ts +declare var module: { exports: any}; +// @Filename: mod1.js +/// +/** @typedef {{ type: "a", x: 1 }} A */ +/** @typedef {{ type: "b", y: 1 }} B */ +/** @typedef {A | B} Both */ +module.exports = C +function C() { + this.p = 1 +} + +// @Filename: mod2.js +/// +/** @typedef {{ type: "a", x: 1 }} A */ +/** @typedef {{ type: "b", y: 1 }} B */ +/** @typedef {A | B} Both */ + +export function C() { + this.p = 1 +} + +// @Filename: mod3.js +/// +/** @typedef {{ type: "a", x: 1 }} A */ +/** @typedef {{ type: "b", y: 1 }} B */ +/** @typedef {A | B} Both */ + +exports.C = function() { + this.p = 1 +} + +// @Filename: use.js +/** @type {import('./mod1').Both} */ +var both1 = { type: 'a', x: 1 }; +/** @type {import('./mod2').Both} */ +var both2 = both1; +/** @type {import('./mod3').Both} */ +var both3 = both2; + + From 539afc0bf023d4a338cff99ed3f6b8065f3a5b03 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 27 Apr 2018 10:10:34 -0700 Subject: [PATCH 2/5] Post-bind typedef instead of pre-checking for commonjs --- src/compiler/binder.ts | 25 +++++++++++++++++-- src/compiler/parser.ts | 13 ---------- .../reference/jsdocTypedefNoCrash.symbols | 2 +- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 2e94c84596717..136a86d0429bd 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -118,6 +118,7 @@ namespace ts { let thisParentContainer: Node; // Container one level up let blockScopeContainer: Node; let lastContainer: Node; + let delayedTypedefs: { typedef: JSDocTypedefTag, container: Node, lastContainer: Node, blockScopeContainer: Node, parent: Node }[]; let seenThisKeyword: boolean; // state used by control flow analysis @@ -176,6 +177,7 @@ namespace ts { bind(file); file.symbolCount = symbolCount; file.classifiableNames = classifiableNames; + delayedBindJSDocTypedefTag(); } file = undefined; @@ -186,6 +188,7 @@ namespace ts { thisParentContainer = undefined; blockScopeContainer = undefined; lastContainer = undefined; + delayedTypedefs = undefined; seenThisKeyword = false; currentFlow = undefined; currentBreakTarget = undefined; @@ -1744,6 +1747,24 @@ namespace ts { bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes); } + function delayedBindJSDocTypedefTag() { + if (!delayedTypedefs) { + return; + } + const saveContainer = container; + const saveLastContainer = lastContainer; + const saveBlockScopeContainer = blockScopeContainer; + const saveParent = parent; + for (const delay of delayedTypedefs) { + ({ container, lastContainer, blockScopeContainer, parent } = delay); + bindBlockScopedDeclaration(delay.typedef, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); + } + container = saveContainer; + lastContainer = saveLastContainer; + blockScopeContainer = saveBlockScopeContainer; + parent = saveParent; + } + // The binder visits every node in the syntax tree so it is a convenient place to perform a single localized // check for reserved words used as identifiers in strict mode code. function checkStrictModeIdentifier(node: Identifier) { @@ -2193,7 +2214,7 @@ namespace ts { case SyntaxKind.JSDocTypedefTag: { const { fullName } = node as JSDocTypedefTag; if (!fullName || fullName.kind === SyntaxKind.Identifier) { - return bindBlockScopedDeclaration(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); + (delayedTypedefs || (delayedTypedefs = [])).push({ typedef: node as JSDocTypedefTag, container, lastContainer, blockScopeContainer, parent }); } break; } @@ -2210,7 +2231,7 @@ namespace ts { function bindSourceFileIfExternalModule() { setExportContextFlag(file); - if (isExternalOrCommonJsModule(file)) { + if (isExternalModule(file)) { bindSourceFileAsExternalModule(); } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b67efe9dfb8c9..8547eb171147a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6116,19 +6116,6 @@ namespace ts { || node.kind === SyntaxKind.ExportDeclaration ? node : undefined); - if (isInJavaScriptFile(sourceFile) && !sourceFile.externalModuleIndicator) { - sourceFile.commonJsModuleIndicator = forEach(sourceFile.statements, isCommonJsModuleIndicator); - } - } - - function isCommonJsModuleIndicator(statement: Statement) { - if (isExpressionStatement(statement) && isBinaryExpression(statement.expression)) { - const special = getSpecialPropertyAssignmentKind(statement.expression); - if (special === SpecialPropertyAssignmentKind.ModuleExports || - special === SpecialPropertyAssignmentKind.ExportsProperty) { - return statement.expression.left; - } - } } const enum ParsingContext { diff --git a/tests/baselines/reference/jsdocTypedefNoCrash.symbols b/tests/baselines/reference/jsdocTypedefNoCrash.symbols index 43ba5734073cc..51f5fdcc259fc 100644 --- a/tests/baselines/reference/jsdocTypedefNoCrash.symbols +++ b/tests/baselines/reference/jsdocTypedefNoCrash.symbols @@ -4,5 +4,5 @@ * }} */ export const foo = 5; ->foo : Symbol(foo, Decl(export.js, 1, 3), Decl(export.js, 4, 12)) +>foo : Symbol(foo, Decl(export.js, 4, 12), Decl(export.js, 1, 3)) From 777b5a089b88d1c2501d55a1393ed2a62d72fd1a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 27 Apr 2018 16:00:47 -0700 Subject: [PATCH 3/5] Duplicate identifier errors --- src/compiler/binder.ts | 5 +- src/compiler/checker.ts | 10 ++ .../reference/typedefCrossModule2.errors.txt | 62 +++++++++++++ .../reference/typedefCrossModule2.symbols | 77 ++++++++++++++++ .../reference/typedefCrossModule2.types | 92 +++++++++++++++++++ .../conformance/jsdoc/typedefCrossModule2.ts | 45 +++++++++ 6 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/typedefCrossModule2.errors.txt create mode 100644 tests/baselines/reference/typedefCrossModule2.symbols create mode 100644 tests/baselines/reference/typedefCrossModule2.types create mode 100644 tests/cases/conformance/jsdoc/typedefCrossModule2.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 136a86d0429bd..d470a9d778509 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2324,7 +2324,10 @@ namespace ts { return s; }); if (symbol) { - declareSymbol(symbol.exports, symbol, lhs, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None); + const flags = isClassExpression(node.right) ? + SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.Class : + SymbolFlags.Property | SymbolFlags.ExportValue; + declareSymbol(symbol.exports, symbol, lhs, flags, SymbolFlags.None); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fa485b68a9237..afd5ba66c24e5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27220,6 +27220,16 @@ namespace ts { let currentKind: Flags; switch (prop.kind) { case SyntaxKind.PropertyAssignment: + if (isClassExpression(prop.initializer)) { + const n = prop.name; + const sn = getTextOfPropertyName(n); + const symbol = resolveName(n, sn, SymbolFlags.Type, undefined, sn, /*isUse*/ false); + if (symbol) { + grammarErrorOnNode(symbol.declarations[0], Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(sn)); + return grammarErrorOnNode(n, Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(sn)); + } + } + // falls through case SyntaxKind.ShorthandPropertyAssignment: // Grammar checking for computedPropertyName and shorthandPropertyAssignment checkGrammarForInvalidQuestionMark(prop.questionToken, Diagnostics.An_object_member_cannot_be_declared_optional); diff --git a/tests/baselines/reference/typedefCrossModule2.errors.txt b/tests/baselines/reference/typedefCrossModule2.errors.txt new file mode 100644 index 0000000000000..1d7fbb3bde265 --- /dev/null +++ b/tests/baselines/reference/typedefCrossModule2.errors.txt @@ -0,0 +1,62 @@ +tests/cases/conformance/jsdoc/mod1.js(5,23): error TS2300: Duplicate identifier 'Foo'. +tests/cases/conformance/jsdoc/mod1.js(6,7): error TS2300: Duplicate identifier 'Foo'. +tests/cases/conformance/jsdoc/mod1.js(8,23): error TS2300: Duplicate identifier 'Bar'. +tests/cases/conformance/jsdoc/mod1.js(9,9): error TS2300: Duplicate identifier 'Bar'. +tests/cases/conformance/jsdoc/mod1.js(11,5): error TS2300: Duplicate identifier 'Baz'. +tests/cases/conformance/jsdoc/mod1.js(13,5): error TS2300: Duplicate identifier 'Baz'. + + +==== tests/cases/conformance/jsdoc/use.js (0 errors) ==== + var mod = require('./mod1.js'); + /** @type {import("./mod1.js").Baz} */ + var b; + /** @type {mod.Baz} */ + var bb; + var bbb = new mod.Baz(); + +==== tests/cases/conformance/jsdoc/commonjs.d.ts (0 errors) ==== + declare function require(name: string): any; + declare var module: { exports: any}; + declare var exports: any; + +==== tests/cases/conformance/jsdoc/mod1.js (6 errors) ==== + /// + + // error + + /** @typedef {number} Foo */ + ~~~ +!!! error TS2300: Duplicate identifier 'Foo'. + class Foo { } // should error + ~~~ +!!! error TS2300: Duplicate identifier 'Foo'. + + /** @typedef {number} Bar */ + ~~~ +!!! error TS2300: Duplicate identifier 'Bar'. + exports.Bar = class { } + ~~~ +!!! error TS2300: Duplicate identifier 'Bar'. + + /** @typedef {number} Baz */ + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2300: Duplicate identifier 'Baz'. + module.exports = { + Baz: class { } + ~~~ +!!! error TS2300: Duplicate identifier 'Baz'. + } + + // ok + + /** @typedef {number} Qux */ + var Qux = 2; + + /** @typedef {number} Quid */ + exports.Quid = 2; + + /** @typedef {number} Quack */ + module.exports = { + Quack: 2 + } + \ No newline at end of file diff --git a/tests/baselines/reference/typedefCrossModule2.symbols b/tests/baselines/reference/typedefCrossModule2.symbols new file mode 100644 index 0000000000000..5c51828176b97 --- /dev/null +++ b/tests/baselines/reference/typedefCrossModule2.symbols @@ -0,0 +1,77 @@ +=== tests/cases/conformance/jsdoc/use.js === +var mod = require('./mod1.js'); +>mod : Symbol(mod, Decl(use.js, 0, 3)) +>require : Symbol(require, Decl(commonjs.d.ts, 0, 0)) +>'./mod1.js' : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) + +/** @type {import("./mod1.js").Baz} */ +var b; +>b : Symbol(b, Decl(use.js, 2, 3)) + +/** @type {mod.Baz} */ +var bb; +>bb : Symbol(bb, Decl(use.js, 4, 3)) + +var bbb = new mod.Baz(); +>bbb : Symbol(bbb, Decl(use.js, 5, 3)) +>mod : Symbol(mod, Decl(use.js, 0, 3)) + +=== tests/cases/conformance/jsdoc/commonjs.d.ts === +declare function require(name: string): any; +>require : Symbol(require, Decl(commonjs.d.ts, 0, 0)) +>name : Symbol(name, Decl(commonjs.d.ts, 0, 25)) + +declare var module: { exports: any}; +>module : Symbol(module, Decl(commonjs.d.ts, 1, 11)) +>exports : Symbol(exports, Decl(commonjs.d.ts, 1, 21)) + +declare var exports: any; +>exports : Symbol(exports, Decl(commonjs.d.ts, 2, 11)) + +=== tests/cases/conformance/jsdoc/mod1.js === +/// + +// error + +/** @typedef {number} Foo */ +class Foo { } // should error +>Foo : Symbol(Foo, Decl(mod1.js, 0, 0)) + +/** @typedef {number} Bar */ +exports.Bar = class { } +>exports.Bar : Symbol(Bar, Decl(mod1.js, 5, 13)) +>exports : Symbol(Bar, Decl(mod1.js, 5, 13)) +>Bar : Symbol(Bar, Decl(mod1.js, 5, 13)) + +/** @typedef {number} Baz */ +module.exports = { +>module.exports : Symbol(exports, Decl(commonjs.d.ts, 1, 21)) +>module : Symbol(export=, Decl(mod1.js, 8, 23), Decl(mod1.js, 21, 17)) +>exports : Symbol(export=, Decl(mod1.js, 8, 23), Decl(mod1.js, 21, 17)) + + Baz: class { } +>Baz : Symbol(Baz, Decl(mod1.js, 11, 18)) +} + +// ok + +/** @typedef {number} Qux */ +var Qux = 2; +>Qux : Symbol(Qux, Decl(mod1.js, 18, 3), Decl(mod1.js, 17, 4)) + +/** @typedef {number} Quid */ +exports.Quid = 2; +>exports.Quid : Symbol(Quid, Decl(mod1.js, 18, 12), Decl(mod1.js, 20, 4)) +>exports : Symbol(Quid, Decl(mod1.js, 18, 12), Decl(mod1.js, 20, 4)) +>Quid : Symbol(Quid, Decl(mod1.js, 18, 12), Decl(mod1.js, 20, 4)) + +/** @typedef {number} Quack */ +module.exports = { +>module.exports : Symbol(exports, Decl(commonjs.d.ts, 1, 21)) +>module : Symbol(export=, Decl(mod1.js, 8, 23), Decl(mod1.js, 21, 17)) +>exports : Symbol(export=, Decl(mod1.js, 8, 23), Decl(mod1.js, 21, 17)) + + Quack: 2 +>Quack : Symbol(Quack, Decl(mod1.js, 24, 18)) +} + diff --git a/tests/baselines/reference/typedefCrossModule2.types b/tests/baselines/reference/typedefCrossModule2.types new file mode 100644 index 0000000000000..2cf4314c33599 --- /dev/null +++ b/tests/baselines/reference/typedefCrossModule2.types @@ -0,0 +1,92 @@ +=== tests/cases/conformance/jsdoc/use.js === +var mod = require('./mod1.js'); +>mod : { [x: string]: any; Baz: any; Bar: typeof Bar; Quid: any; } | { [x: string]: any; Quack: any; Bar: typeof Bar; Quid: any; } +>require('./mod1.js') : { [x: string]: any; Baz: any; Bar: typeof Bar; Quid: any; } | { [x: string]: any; Quack: any; Bar: typeof Bar; Quid: any; } +>require : (name: string) => any +>'./mod1.js' : "./mod1.js" + +/** @type {import("./mod1.js").Baz} */ +var b; +>b : number + +/** @type {mod.Baz} */ +var bb; +>bb : number + +var bbb = new mod.Baz(); +>bbb : any +>new mod.Baz() : any +>mod.Baz : any +>mod : { [x: string]: any; Baz: any; Bar: typeof Bar; Quid: any; } | { [x: string]: any; Quack: any; Bar: typeof Bar; Quid: any; } +>Baz : any + +=== tests/cases/conformance/jsdoc/commonjs.d.ts === +declare function require(name: string): any; +>require : (name: string) => any +>name : string + +declare var module: { exports: any}; +>module : { exports: any; } +>exports : any + +declare var exports: any; +>exports : any + +=== tests/cases/conformance/jsdoc/mod1.js === +/// + +// error + +/** @typedef {number} Foo */ +class Foo { } // should error +>Foo : Foo + +/** @typedef {number} Bar */ +exports.Bar = class { } +>exports.Bar = class { } : typeof Bar +>exports.Bar : typeof Bar +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>Bar : typeof Bar +>class { } : typeof Bar + +/** @typedef {number} Baz */ +module.exports = { +>module.exports = { Baz: class { }} : { [x: string]: any; Baz: typeof Baz; } +>module.exports : any +>module : { exports: any; } +>exports : any +>{ Baz: class { }} : { [x: string]: any; Baz: typeof Baz; } + + Baz: class { } +>Baz : typeof Baz +>class { } : typeof Baz +} + +// ok + +/** @typedef {number} Qux */ +var Qux = 2; +>Qux : number +>2 : 2 + +/** @typedef {number} Quid */ +exports.Quid = 2; +>exports.Quid = 2 : 2 +>exports.Quid : any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>Quid : any +>2 : 2 + +/** @typedef {number} Quack */ +module.exports = { +>module.exports = { Quack: 2} : { [x: string]: any; Quack: number; } +>module.exports : any +>module : { exports: any; } +>exports : any +>{ Quack: 2} : { [x: string]: any; Quack: number; } + + Quack: 2 +>Quack : number +>2 : 2 +} + diff --git a/tests/cases/conformance/jsdoc/typedefCrossModule2.ts b/tests/cases/conformance/jsdoc/typedefCrossModule2.ts new file mode 100644 index 0000000000000..99398f4995623 --- /dev/null +++ b/tests/cases/conformance/jsdoc/typedefCrossModule2.ts @@ -0,0 +1,45 @@ +// @noEmit: true +// @allowJs: true +// @checkJs: true +// @Filename: commonjs.d.ts +declare function require(name: string): any; +declare var module: { exports: any}; +declare var exports: any; + +// @Filename: mod1.js +/// + +// error + +/** @typedef {number} Foo */ +class Foo { } // should error + +/** @typedef {number} Bar */ +exports.Bar = class { } + +/** @typedef {number} Baz */ +module.exports = { + Baz: class { } +} + +// ok + +/** @typedef {number} Qux */ +var Qux = 2; + +/** @typedef {number} Quid */ +exports.Quid = 2; + +/** @typedef {number} Quack */ +module.exports = { + Quack: 2 +} + +// @Filename: use.js + +var mod = require('./mod1.js'); +/** @type {import("./mod1.js").Baz} */ +var b; +/** @type {mod.Baz} */ +var bb; +var bbb = new mod.Baz(); From 0f797a1e4e4680c8980ebdcd4a7be3916706dce4 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 30 Apr 2018 12:24:40 -0700 Subject: [PATCH 4/5] Fix class type reference resolution+update baselines --- src/compiler/checker.ts | 31 ++++++++++++++----- .../fourslash/findAllRefsClassExpression2.ts | 4 +-- ...refactorConvertToEs6Module_export_named.ts | 4 ++- ...ToEs6Module_export_namedClassExpression.ts | 10 ++++-- ...vertToEs6Module_expressionToDeclaration.ts | 5 ++- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index afd5ba66c24e5..5f8d071f93fe3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7562,23 +7562,38 @@ namespace ts { return unknownType; } - // A jsdoc TypeReference may have resolved to a value (as opposed to a type). If - // the symbol is a constructor function, return the inferred class type; otherwise, - // the type of this reference is just the type of the value we resolved to. + const jsdocType = getJSDocTypeReference(node, symbol, typeArguments); + if (jsdocType) { + return jsdocType; + } + + // Resolve the type reference as a Type for the purpose of reporting errors. + resolveTypeReferenceName(getTypeReferenceName(node), SymbolFlags.Type); + return getTypeOfSymbol(symbol); + } + + /** + * A jsdoc TypeReference may have resolved to a value (as opposed to a type). If + * the symbol is a constructor function, return the inferred class type; otherwise, + * the type of this reference is just the type of the value we resolved to. + */ + function getJSDocTypeReference(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[]): Type | undefined { const assignedType = getAssignedClassType(symbol); const valueType = getTypeOfSymbol(symbol); - const referenceType = valueType.symbol && !isInferredClassType(valueType) && getTypeReferenceTypeWorker(node, valueType.symbol, typeArguments); + const referenceType = valueType.symbol && valueType.symbol !== symbol && !isInferredClassType(valueType) && getTypeReferenceTypeWorker(node, valueType.symbol, typeArguments); if (referenceType || assignedType) { return referenceType && assignedType ? getIntersectionType([assignedType, referenceType]) : referenceType || assignedType; } - - // Resolve the type reference as a Type for the purpose of reporting errors. - resolveTypeReferenceName(getTypeReferenceName(node), SymbolFlags.Type); - return valueType; } function getTypeReferenceTypeWorker(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[]): Type | undefined { if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { + if (symbol.valueDeclaration && isBinaryExpression(symbol.valueDeclaration.parent)) { + const jsdocType = getJSDocTypeReference(node, symbol, typeArguments); + if (jsdocType) { + return jsdocType; + } + } return getTypeFromClassOrInterfaceReference(node, symbol, typeArguments); } diff --git a/tests/cases/fourslash/findAllRefsClassExpression2.ts b/tests/cases/fourslash/findAllRefsClassExpression2.ts index f25e64a2c68e8..1214e3f7c8856 100644 --- a/tests/cases/fourslash/findAllRefsClassExpression2.ts +++ b/tests/cases/fourslash/findAllRefsClassExpression2.ts @@ -10,7 +10,7 @@ ////[|A|]; const [r0, r1, r2] = test.ranges(); -const defs = { definition: "(property) A: typeof A", ranges: [r0] }; -const imports = { definition: "(alias) (property) A: typeof A\nimport A", ranges: [r1, r2] }; +const defs = { definition: "class A\n(property) A: typeof A", ranges: [r0] }; +const imports = { definition: "(alias) class A\n(alias) (property) A: typeof A\nimport A", ranges: [r1, r2] }; verify.referenceGroups([r0], [defs, imports]); verify.referenceGroups([r1, r2], [imports, defs]); diff --git a/tests/cases/fourslash/refactorConvertToEs6Module_export_named.ts b/tests/cases/fourslash/refactorConvertToEs6Module_export_named.ts index e7ee290858038..f8eba2b02134b 100644 --- a/tests/cases/fourslash/refactorConvertToEs6Module_export_named.ts +++ b/tests/cases/fourslash/refactorConvertToEs6Module_export_named.ts @@ -21,7 +21,9 @@ verify.codeFix({ description: "Convert to ES6 module", newFileContent: `export function f() {} -export class C {} +const _C = class { +}; +export { _C as C }; export const x = 0; export function a1() {} export function a2() { return 0; } diff --git a/tests/cases/fourslash/refactorConvertToEs6Module_export_namedClassExpression.ts b/tests/cases/fourslash/refactorConvertToEs6Module_export_namedClassExpression.ts index e1cb86e8d682e..47b8a5e0b32cf 100644 --- a/tests/cases/fourslash/refactorConvertToEs6Module_export_namedClassExpression.ts +++ b/tests/cases/fourslash/refactorConvertToEs6Module_export_namedClassExpression.ts @@ -10,6 +10,12 @@ verify.codeFix({ description: "Convert to ES6 module", newFileContent: -`export const C = class E { static instance = new E(); } -export class D { static instance = new D(); }`, +`const _C = class E { + static instance = new E(); +}; +export { _C as C }; +const _D = class D { + static instance = new D(); +}; +export { _D as D };`, }); diff --git a/tests/cases/fourslash/refactorConvertToEs6Module_expressionToDeclaration.ts b/tests/cases/fourslash/refactorConvertToEs6Module_expressionToDeclaration.ts index 47ff26f459ed2..b360334767f9e 100644 --- a/tests/cases/fourslash/refactorConvertToEs6Module_expressionToDeclaration.ts +++ b/tests/cases/fourslash/refactorConvertToEs6Module_expressionToDeclaration.ts @@ -11,5 +11,8 @@ verify.codeFix({ description: "Convert to ES6 module", newFileContent: `export async function* f(p) { p; } -export class C extends D { m() {} }`, +const _C = class C extends D { + m() { } +}; +export { _C as C };`, }); From f733b21a460e7c29042f6f266d046fe76dba59ae Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 30 Apr 2018 13:09:15 -0700 Subject: [PATCH 5/5] Move to a type-based check for duplicate identifiers --- src/compiler/checker.ts | 29 ++++++++----- .../reference/typedefCrossModule2.errors.txt | 32 +++++++------- .../reference/typedefCrossModule2.symbols | 43 ++++++------------- .../reference/typedefCrossModule2.types | 20 ++------- .../reference/typedefCrossModule3.errors.txt | 18 ++++++++ .../reference/typedefCrossModule3.symbols | 16 +++++++ .../reference/typedefCrossModule3.types | 21 +++++++++ .../reference/typedefCrossModule4.errors.txt | 17 ++++++++ .../reference/typedefCrossModule4.symbols | 12 ++++++ .../reference/typedefCrossModule4.types | 15 +++++++ .../conformance/jsdoc/typedefCrossModule2.ts | 6 --- .../conformance/jsdoc/typedefCrossModule3.ts | 10 +++++ .../conformance/jsdoc/typedefCrossModule4.ts | 9 ++++ 13 files changed, 170 insertions(+), 78 deletions(-) create mode 100644 tests/baselines/reference/typedefCrossModule3.errors.txt create mode 100644 tests/baselines/reference/typedefCrossModule3.symbols create mode 100644 tests/baselines/reference/typedefCrossModule3.types create mode 100644 tests/baselines/reference/typedefCrossModule4.errors.txt create mode 100644 tests/baselines/reference/typedefCrossModule4.symbols create mode 100644 tests/baselines/reference/typedefCrossModule4.types create mode 100644 tests/cases/conformance/jsdoc/typedefCrossModule3.ts create mode 100644 tests/cases/conformance/jsdoc/typedefCrossModule4.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5f8d071f93fe3..3b55883f1317f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19930,6 +19930,7 @@ namespace ts { getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], UnionReduction.Subtype) : leftType; case SyntaxKind.EqualsToken: + checkSpecialAssignment(left, right); checkAssignmentOperator(rightType); return getRegularTypeOfObjectLiteral(rightType); case SyntaxKind.CommaToken: @@ -19939,6 +19940,24 @@ namespace ts { return rightType; } + function checkSpecialAssignment(left: Node, right: Expression) { + const special = getSpecialPropertyAssignmentKind(left.parent as BinaryExpression); + if (special === SpecialPropertyAssignmentKind.ModuleExports) { + const rightType = checkExpression(right, checkMode); + for (const prop of getPropertiesOfObjectType(rightType)) { + const propType = getTypeOfSymbol(prop); + if (propType.symbol && propType.symbol.flags & SymbolFlags.Class) { + const name = prop.escapedName; + const symbol = resolveName(prop.valueDeclaration, name, SymbolFlags.Type, undefined, name, /*isUse*/ false); + if (symbol) { + grammarErrorOnNode(symbol.declarations[0], Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(name)); + return grammarErrorOnNode(prop.valueDeclaration, Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(name)); + } + } + } + } + } + function isEvalNode(node: Expression) { return node.kind === SyntaxKind.Identifier && (node as Identifier).escapedText === "eval"; } @@ -27235,16 +27254,6 @@ namespace ts { let currentKind: Flags; switch (prop.kind) { case SyntaxKind.PropertyAssignment: - if (isClassExpression(prop.initializer)) { - const n = prop.name; - const sn = getTextOfPropertyName(n); - const symbol = resolveName(n, sn, SymbolFlags.Type, undefined, sn, /*isUse*/ false); - if (symbol) { - grammarErrorOnNode(symbol.declarations[0], Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(sn)); - return grammarErrorOnNode(n, Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(sn)); - } - } - // falls through case SyntaxKind.ShorthandPropertyAssignment: // Grammar checking for computedPropertyName and shorthandPropertyAssignment checkGrammarForInvalidQuestionMark(prop.questionToken, Diagnostics.An_object_member_cannot_be_declared_optional); diff --git a/tests/baselines/reference/typedefCrossModule2.errors.txt b/tests/baselines/reference/typedefCrossModule2.errors.txt index 1d7fbb3bde265..04d28448e5795 100644 --- a/tests/baselines/reference/typedefCrossModule2.errors.txt +++ b/tests/baselines/reference/typedefCrossModule2.errors.txt @@ -1,27 +1,25 @@ -tests/cases/conformance/jsdoc/mod1.js(5,23): error TS2300: Duplicate identifier 'Foo'. -tests/cases/conformance/jsdoc/mod1.js(6,7): error TS2300: Duplicate identifier 'Foo'. -tests/cases/conformance/jsdoc/mod1.js(8,23): error TS2300: Duplicate identifier 'Bar'. -tests/cases/conformance/jsdoc/mod1.js(9,9): error TS2300: Duplicate identifier 'Bar'. +tests/cases/conformance/jsdoc/mod1.js(3,23): error TS2300: Duplicate identifier 'Foo'. +tests/cases/conformance/jsdoc/mod1.js(4,7): error TS2300: Duplicate identifier 'Foo'. +tests/cases/conformance/jsdoc/mod1.js(6,23): error TS2300: Duplicate identifier 'Bar'. +tests/cases/conformance/jsdoc/mod1.js(7,9): error TS2300: Duplicate identifier 'Bar'. +tests/cases/conformance/jsdoc/mod1.js(9,5): error TS2300: Duplicate identifier 'Baz'. +tests/cases/conformance/jsdoc/mod1.js(10,1): error TS2304: Cannot find name 'module'. tests/cases/conformance/jsdoc/mod1.js(11,5): error TS2300: Duplicate identifier 'Baz'. -tests/cases/conformance/jsdoc/mod1.js(13,5): error TS2300: Duplicate identifier 'Baz'. +tests/cases/conformance/jsdoc/mod1.js(23,1): error TS2304: Cannot find name 'module'. +tests/cases/conformance/jsdoc/use.js(1,11): error TS2304: Cannot find name 'require'. -==== tests/cases/conformance/jsdoc/use.js (0 errors) ==== +==== tests/cases/conformance/jsdoc/use.js (1 errors) ==== var mod = require('./mod1.js'); + ~~~~~~~ +!!! error TS2304: Cannot find name 'require'. /** @type {import("./mod1.js").Baz} */ var b; /** @type {mod.Baz} */ var bb; var bbb = new mod.Baz(); -==== tests/cases/conformance/jsdoc/commonjs.d.ts (0 errors) ==== - declare function require(name: string): any; - declare var module: { exports: any}; - declare var exports: any; - -==== tests/cases/conformance/jsdoc/mod1.js (6 errors) ==== - /// - +==== tests/cases/conformance/jsdoc/mod1.js (8 errors) ==== // error /** @typedef {number} Foo */ @@ -42,8 +40,10 @@ tests/cases/conformance/jsdoc/mod1.js(13,5): error TS2300: Duplicate identifier ~~~~~~~~~~~~~~~~~~~~~ !!! error TS2300: Duplicate identifier 'Baz'. module.exports = { + ~~~~~~ +!!! error TS2304: Cannot find name 'module'. Baz: class { } - ~~~ + ~~~~~~~~~~~~~~ !!! error TS2300: Duplicate identifier 'Baz'. } @@ -57,6 +57,8 @@ tests/cases/conformance/jsdoc/mod1.js(13,5): error TS2300: Duplicate identifier /** @typedef {number} Quack */ module.exports = { + ~~~~~~ +!!! error TS2304: Cannot find name 'module'. Quack: 2 } \ No newline at end of file diff --git a/tests/baselines/reference/typedefCrossModule2.symbols b/tests/baselines/reference/typedefCrossModule2.symbols index 5c51828176b97..01afe2b9bdb75 100644 --- a/tests/baselines/reference/typedefCrossModule2.symbols +++ b/tests/baselines/reference/typedefCrossModule2.symbols @@ -1,7 +1,6 @@ === tests/cases/conformance/jsdoc/use.js === var mod = require('./mod1.js'); >mod : Symbol(mod, Decl(use.js, 0, 3)) ->require : Symbol(require, Decl(commonjs.d.ts, 0, 0)) >'./mod1.js' : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) /** @type {import("./mod1.js").Baz} */ @@ -16,21 +15,7 @@ var bbb = new mod.Baz(); >bbb : Symbol(bbb, Decl(use.js, 5, 3)) >mod : Symbol(mod, Decl(use.js, 0, 3)) -=== tests/cases/conformance/jsdoc/commonjs.d.ts === -declare function require(name: string): any; ->require : Symbol(require, Decl(commonjs.d.ts, 0, 0)) ->name : Symbol(name, Decl(commonjs.d.ts, 0, 25)) - -declare var module: { exports: any}; ->module : Symbol(module, Decl(commonjs.d.ts, 1, 11)) ->exports : Symbol(exports, Decl(commonjs.d.ts, 1, 21)) - -declare var exports: any; ->exports : Symbol(exports, Decl(commonjs.d.ts, 2, 11)) - === tests/cases/conformance/jsdoc/mod1.js === -/// - // error /** @typedef {number} Foo */ @@ -39,39 +24,37 @@ class Foo { } // should error /** @typedef {number} Bar */ exports.Bar = class { } ->exports.Bar : Symbol(Bar, Decl(mod1.js, 5, 13)) ->exports : Symbol(Bar, Decl(mod1.js, 5, 13)) ->Bar : Symbol(Bar, Decl(mod1.js, 5, 13)) +>exports.Bar : Symbol(Bar, Decl(mod1.js, 3, 13)) +>exports : Symbol(Bar, Decl(mod1.js, 3, 13)) +>Bar : Symbol(Bar, Decl(mod1.js, 3, 13)) /** @typedef {number} Baz */ module.exports = { ->module.exports : Symbol(exports, Decl(commonjs.d.ts, 1, 21)) ->module : Symbol(export=, Decl(mod1.js, 8, 23), Decl(mod1.js, 21, 17)) ->exports : Symbol(export=, Decl(mod1.js, 8, 23), Decl(mod1.js, 21, 17)) +>module : Symbol(export=, Decl(mod1.js, 6, 23), Decl(mod1.js, 19, 17)) +>exports : Symbol(export=, Decl(mod1.js, 6, 23), Decl(mod1.js, 19, 17)) Baz: class { } ->Baz : Symbol(Baz, Decl(mod1.js, 11, 18)) +>Baz : Symbol(Baz, Decl(mod1.js, 9, 18)) } // ok /** @typedef {number} Qux */ var Qux = 2; ->Qux : Symbol(Qux, Decl(mod1.js, 18, 3), Decl(mod1.js, 17, 4)) +>Qux : Symbol(Qux, Decl(mod1.js, 16, 3), Decl(mod1.js, 15, 4)) /** @typedef {number} Quid */ exports.Quid = 2; ->exports.Quid : Symbol(Quid, Decl(mod1.js, 18, 12), Decl(mod1.js, 20, 4)) ->exports : Symbol(Quid, Decl(mod1.js, 18, 12), Decl(mod1.js, 20, 4)) ->Quid : Symbol(Quid, Decl(mod1.js, 18, 12), Decl(mod1.js, 20, 4)) +>exports.Quid : Symbol(Quid, Decl(mod1.js, 16, 12), Decl(mod1.js, 18, 4)) +>exports : Symbol(Quid, Decl(mod1.js, 16, 12), Decl(mod1.js, 18, 4)) +>Quid : Symbol(Quid, Decl(mod1.js, 16, 12), Decl(mod1.js, 18, 4)) /** @typedef {number} Quack */ module.exports = { ->module.exports : Symbol(exports, Decl(commonjs.d.ts, 1, 21)) ->module : Symbol(export=, Decl(mod1.js, 8, 23), Decl(mod1.js, 21, 17)) ->exports : Symbol(export=, Decl(mod1.js, 8, 23), Decl(mod1.js, 21, 17)) +>module : Symbol(export=, Decl(mod1.js, 6, 23), Decl(mod1.js, 19, 17)) +>exports : Symbol(export=, Decl(mod1.js, 6, 23), Decl(mod1.js, 19, 17)) Quack: 2 ->Quack : Symbol(Quack, Decl(mod1.js, 24, 18)) +>Quack : Symbol(Quack, Decl(mod1.js, 22, 18)) } diff --git a/tests/baselines/reference/typedefCrossModule2.types b/tests/baselines/reference/typedefCrossModule2.types index 2cf4314c33599..67e4b7a4e890e 100644 --- a/tests/baselines/reference/typedefCrossModule2.types +++ b/tests/baselines/reference/typedefCrossModule2.types @@ -2,7 +2,7 @@ var mod = require('./mod1.js'); >mod : { [x: string]: any; Baz: any; Bar: typeof Bar; Quid: any; } | { [x: string]: any; Quack: any; Bar: typeof Bar; Quid: any; } >require('./mod1.js') : { [x: string]: any; Baz: any; Bar: typeof Bar; Quid: any; } | { [x: string]: any; Quack: any; Bar: typeof Bar; Quid: any; } ->require : (name: string) => any +>require : any >'./mod1.js' : "./mod1.js" /** @type {import("./mod1.js").Baz} */ @@ -20,21 +20,7 @@ var bbb = new mod.Baz(); >mod : { [x: string]: any; Baz: any; Bar: typeof Bar; Quid: any; } | { [x: string]: any; Quack: any; Bar: typeof Bar; Quid: any; } >Baz : any -=== tests/cases/conformance/jsdoc/commonjs.d.ts === -declare function require(name: string): any; ->require : (name: string) => any ->name : string - -declare var module: { exports: any}; ->module : { exports: any; } ->exports : any - -declare var exports: any; ->exports : any - === tests/cases/conformance/jsdoc/mod1.js === -/// - // error /** @typedef {number} Foo */ @@ -53,7 +39,7 @@ exports.Bar = class { } module.exports = { >module.exports = { Baz: class { }} : { [x: string]: any; Baz: typeof Baz; } >module.exports : any ->module : { exports: any; } +>module : any >exports : any >{ Baz: class { }} : { [x: string]: any; Baz: typeof Baz; } @@ -81,7 +67,7 @@ exports.Quid = 2; module.exports = { >module.exports = { Quack: 2} : { [x: string]: any; Quack: number; } >module.exports : any ->module : { exports: any; } +>module : any >exports : any >{ Quack: 2} : { [x: string]: any; Quack: number; } diff --git a/tests/baselines/reference/typedefCrossModule3.errors.txt b/tests/baselines/reference/typedefCrossModule3.errors.txt new file mode 100644 index 0000000000000..5309528fb4e6c --- /dev/null +++ b/tests/baselines/reference/typedefCrossModule3.errors.txt @@ -0,0 +1,18 @@ +tests/cases/conformance/jsdoc/mod2.js(1,5): error TS2300: Duplicate identifier 'Foo'. +tests/cases/conformance/jsdoc/mod2.js(3,1): error TS2300: Duplicate identifier 'Foo'. +tests/cases/conformance/jsdoc/mod2.js(4,1): error TS2304: Cannot find name 'module'. + + +==== tests/cases/conformance/jsdoc/mod2.js (3 errors) ==== + /** @typedef {number} Foo */ + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2300: Duplicate identifier 'Foo'. + const ns = {}; + ns.Foo = class {} + ~~~~~~ +!!! error TS2300: Duplicate identifier 'Foo'. + module.exports = ns; + ~~~~~~ +!!! error TS2304: Cannot find name 'module'. + + \ No newline at end of file diff --git a/tests/baselines/reference/typedefCrossModule3.symbols b/tests/baselines/reference/typedefCrossModule3.symbols new file mode 100644 index 0000000000000..3e1c8a55535df --- /dev/null +++ b/tests/baselines/reference/typedefCrossModule3.symbols @@ -0,0 +1,16 @@ +=== tests/cases/conformance/jsdoc/mod2.js === +/** @typedef {number} Foo */ +const ns = {}; +>ns : Symbol(ns, Decl(mod2.js, 1, 5), Decl(mod2.js, 1, 14)) + +ns.Foo = class {} +>ns.Foo : Symbol(Foo, Decl(mod2.js, 1, 14)) +>ns : Symbol(ns, Decl(mod2.js, 1, 5), Decl(mod2.js, 1, 14)) +>Foo : Symbol(Foo, Decl(mod2.js, 1, 14)) + +module.exports = ns; +>module : Symbol(export=, Decl(mod2.js, 2, 17)) +>exports : Symbol(export=, Decl(mod2.js, 2, 17)) +>ns : Symbol(ns, Decl(mod2.js, 1, 5), Decl(mod2.js, 1, 14)) + + diff --git a/tests/baselines/reference/typedefCrossModule3.types b/tests/baselines/reference/typedefCrossModule3.types new file mode 100644 index 0000000000000..9d3767dcbbaba --- /dev/null +++ b/tests/baselines/reference/typedefCrossModule3.types @@ -0,0 +1,21 @@ +=== tests/cases/conformance/jsdoc/mod2.js === +/** @typedef {number} Foo */ +const ns = {}; +>ns : { [x: string]: any; Foo: typeof Foo; } +>{} : { [x: string]: any; Foo: typeof Foo; } + +ns.Foo = class {} +>ns.Foo = class {} : typeof Foo +>ns.Foo : typeof Foo +>ns : { [x: string]: any; Foo: typeof Foo; } +>Foo : typeof Foo +>class {} : typeof Foo + +module.exports = ns; +>module.exports = ns : { [x: string]: any; Foo: typeof Foo; } +>module.exports : any +>module : any +>exports : any +>ns : { [x: string]: any; Foo: typeof Foo; } + + diff --git a/tests/baselines/reference/typedefCrossModule4.errors.txt b/tests/baselines/reference/typedefCrossModule4.errors.txt new file mode 100644 index 0000000000000..5dcd9ac017bfe --- /dev/null +++ b/tests/baselines/reference/typedefCrossModule4.errors.txt @@ -0,0 +1,17 @@ +tests/cases/conformance/jsdoc/mod3.js(1,5): error TS2300: Duplicate identifier 'Foo'. +tests/cases/conformance/jsdoc/mod3.js(3,1): error TS2304: Cannot find name 'module'. +tests/cases/conformance/jsdoc/mod3.js(3,20): error TS2300: Duplicate identifier 'Foo'. + + +==== tests/cases/conformance/jsdoc/mod3.js (3 errors) ==== + /** @typedef {number} Foo */ + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2300: Duplicate identifier 'Foo'. + class Bar { } + module.exports = { Foo: Bar }; + ~~~~~~ +!!! error TS2304: Cannot find name 'module'. + ~~~~~~~~ +!!! error TS2300: Duplicate identifier 'Foo'. + + \ No newline at end of file diff --git a/tests/baselines/reference/typedefCrossModule4.symbols b/tests/baselines/reference/typedefCrossModule4.symbols new file mode 100644 index 0000000000000..aa478df301a9c --- /dev/null +++ b/tests/baselines/reference/typedefCrossModule4.symbols @@ -0,0 +1,12 @@ +=== tests/cases/conformance/jsdoc/mod3.js === +/** @typedef {number} Foo */ +class Bar { } +>Bar : Symbol(Bar, Decl(mod3.js, 0, 0)) + +module.exports = { Foo: Bar }; +>module : Symbol(export=, Decl(mod3.js, 1, 13)) +>exports : Symbol(export=, Decl(mod3.js, 1, 13)) +>Foo : Symbol(Foo, Decl(mod3.js, 2, 18)) +>Bar : Symbol(Bar, Decl(mod3.js, 0, 0)) + + diff --git a/tests/baselines/reference/typedefCrossModule4.types b/tests/baselines/reference/typedefCrossModule4.types new file mode 100644 index 0000000000000..a0d14942ef3b5 --- /dev/null +++ b/tests/baselines/reference/typedefCrossModule4.types @@ -0,0 +1,15 @@ +=== tests/cases/conformance/jsdoc/mod3.js === +/** @typedef {number} Foo */ +class Bar { } +>Bar : Bar + +module.exports = { Foo: Bar }; +>module.exports = { Foo: Bar } : { [x: string]: any; Foo: typeof Bar; } +>module.exports : any +>module : any +>exports : any +>{ Foo: Bar } : { [x: string]: any; Foo: typeof Bar; } +>Foo : typeof Bar +>Bar : typeof Bar + + diff --git a/tests/cases/conformance/jsdoc/typedefCrossModule2.ts b/tests/cases/conformance/jsdoc/typedefCrossModule2.ts index 99398f4995623..6bb00957ef396 100644 --- a/tests/cases/conformance/jsdoc/typedefCrossModule2.ts +++ b/tests/cases/conformance/jsdoc/typedefCrossModule2.ts @@ -1,13 +1,7 @@ // @noEmit: true // @allowJs: true // @checkJs: true -// @Filename: commonjs.d.ts -declare function require(name: string): any; -declare var module: { exports: any}; -declare var exports: any; - // @Filename: mod1.js -/// // error diff --git a/tests/cases/conformance/jsdoc/typedefCrossModule3.ts b/tests/cases/conformance/jsdoc/typedefCrossModule3.ts new file mode 100644 index 0000000000000..d6005b541dcb7 --- /dev/null +++ b/tests/cases/conformance/jsdoc/typedefCrossModule3.ts @@ -0,0 +1,10 @@ +// @noEmit: true +// @allowJs: true +// @checkJs: true +// @Filename: mod2.js + +/** @typedef {number} Foo */ +const ns = {}; +ns.Foo = class {} +module.exports = ns; + diff --git a/tests/cases/conformance/jsdoc/typedefCrossModule4.ts b/tests/cases/conformance/jsdoc/typedefCrossModule4.ts new file mode 100644 index 0000000000000..09d27246c7c8f --- /dev/null +++ b/tests/cases/conformance/jsdoc/typedefCrossModule4.ts @@ -0,0 +1,9 @@ +// @noEmit: true +// @allowJs: true +// @checkJs: true +// @Filename: mod3.js + +/** @typedef {number} Foo */ +class Bar { } +module.exports = { Foo: Bar }; +