From ce95d9ca6be1526ffea041150141f9de4ed4b4e6 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 24 Apr 2020 13:49:48 -0700 Subject: [PATCH] Fix values and types merging in JS module exports (#37896) * Fix values and types merging in JS module exports * Fix everything * Share `setValueDeclaration` between binder (local merge) and checker (cross-file merge) * Revert accidental changes to baselines * Update baseline from master merge --- src/compiler/binder.ts | 10 ---- src/compiler/checker.ts | 45 +++++++++++++----- src/compiler/utilities.ts | 11 +++++ ...DeclarationsClassExtendsVisibility.symbols | 2 +- .../jsDeclarationsClassStatic.symbols | 2 +- .../jsDeclarationsCrossfileMerge.symbols | 2 +- ...nedClassExpressionAnonymousWithSub.symbols | 2 +- ...rtAssignedClassExpressionShadowing.symbols | 2 +- ...jsDeclarationsExportSubAssignments.symbols | 2 +- ...MemberMergedWithModuleAugmentation.symbols | 43 +++++++++++++++++ ...rtMemberMergedWithModuleAugmentation.types | 46 +++++++++++++++++++ ...erMergedWithModuleAugmentation2.errors.txt | 27 +++++++++++ ...emberMergedWithModuleAugmentation2.symbols | 25 ++++++++++ ...tMemberMergedWithModuleAugmentation2.types | 31 +++++++++++++ .../reference/moduleExportAlias2.symbols | 2 +- .../reference/moduleExportAlias4.symbols | 2 +- .../moduleExportAliasImported.symbols | 2 +- ...uleExportPropertyAssignmentDefault.symbols | 2 +- ...xportWithExportPropertyAssignment3.symbols | 12 ++--- ...xportWithExportPropertyAssignment4.symbols | 6 +-- .../reference/typedefCrossModule2.types | 26 +++++------ .../reference/typedefCrossModule4.types | 8 ++-- ...xportMemberMergedWithModuleAugmentation.ts | 26 +++++++++++ ...portMemberMergedWithModuleAugmentation2.ts | 17 +++++++ ...mpletionsImportModuleAugmentationWithJS.ts | 41 +++++++++++++++++ 25 files changed, 337 insertions(+), 57 deletions(-) create mode 100644 tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation.symbols create mode 100644 tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation.types create mode 100644 tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.errors.txt create mode 100644 tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.symbols create mode 100644 tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.types create mode 100644 tests/cases/compiler/jsExportMemberMergedWithModuleAugmentation.ts create mode 100644 tests/cases/compiler/jsExportMemberMergedWithModuleAugmentation2.ts create mode 100644 tests/cases/fourslash/completionsImportModuleAugmentationWithJS.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 3cde8be7ea..356b2fd18f 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -320,16 +320,6 @@ namespace ts { } } - function setValueDeclaration(symbol: Symbol, node: Declaration): void { - const { valueDeclaration } = symbol; - if (!valueDeclaration || - (isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node)) || - (valueDeclaration.kind !== node.kind && isEffectiveModuleDeclaration(valueDeclaration))) { - // other kinds of value declarations take precedence over modules and assignment declarations - symbol.valueDeclaration = node; - } - } - // Should not be called on a declaration with a computed property name, // unless it is a well known Symbol. function getDeclarationName(node: Declaration): __String | undefined { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b69320f2e4..f5754baff4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1115,12 +1115,8 @@ namespace ts { target.constEnumOnlyModule = false; } target.flags |= source.flags; - if (source.valueDeclaration && - (!target.valueDeclaration || - isAssignmentDeclaration(target.valueDeclaration) && !isAssignmentDeclaration(source.valueDeclaration) || - isEffectiveModuleDeclaration(target.valueDeclaration) && !isEffectiveModuleDeclaration(source.valueDeclaration))) { - // other kinds of value declarations take precedence over modules and assignment declarations - target.valueDeclaration = source.valueDeclaration; + if (source.valueDeclaration) { + setValueDeclaration(target, source.valueDeclaration); } addRange(target.declarations, source.declarations); if (source.members) { @@ -7724,11 +7720,38 @@ namespace ts { resolvedSymbol.exports = createSymbolTable(); } (resolvedSymbol || symbol).exports!.forEach((s, name) => { - if (members.has(name)) { - const exportedMember = exportedType.members.get(name)!; - const union = createSymbol(s.flags | exportedMember.flags, name); - union.type = getUnionType([getTypeOfSymbol(s), getTypeOfSymbol(exportedMember)]); - members.set(name, union); + const exportedMember = members.get(name)!; + if (exportedMember && exportedMember !== s) { + if (s.flags & SymbolFlags.Value) { + // If the member has an additional value-like declaration, union the types from the two declarations, + // but issue an error if they occurred in two different files. The purpose is to support a JS file with + // a pattern like: + // + // module.exports = { a: true }; + // module.exports.a = 3; + // + // but we may have a JS file with `module.exports = { a: true }` along with a TypeScript module augmentation + // declaring an `export const a: number`. In that case, we issue a duplicate identifier error, because + // it's unclear what that's supposed to mean, so it's probably a mistake. + if (getSourceFileOfNode(s.valueDeclaration) !== getSourceFileOfNode(exportedMember.valueDeclaration)) { + const unescapedName = unescapeLeadingUnderscores(s.escapedName); + const exportedMemberName = tryCast(exportedMember.valueDeclaration, isNamedDeclaration)?.name || exportedMember.valueDeclaration; + addRelatedInfo( + error(s.valueDeclaration, Diagnostics.Duplicate_identifier_0, unescapedName), + createDiagnosticForNode(exportedMemberName, Diagnostics._0_was_also_declared_here, unescapedName)); + addRelatedInfo( + error(exportedMemberName, Diagnostics.Duplicate_identifier_0, unescapedName), + createDiagnosticForNode(s.valueDeclaration, Diagnostics._0_was_also_declared_here, unescapedName)); + } + const union = createSymbol(s.flags | exportedMember.flags, name); + union.type = getUnionType([getTypeOfSymbol(s), getTypeOfSymbol(exportedMember)]); + union.valueDeclaration = exportedMember.valueDeclaration; + union.declarations = concatenate(exportedMember.declarations, s.declarations); + members.set(name, union); + } + else { + members.set(name, mergeSymbol(s, exportedMember)); + } } else { members.set(name, s); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 9e1d338d8d..97231acf60 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2314,6 +2314,17 @@ namespace ts { !!getJSDocTypeTag(expr.parent); } + export function setValueDeclaration(symbol: Symbol, node: Declaration): void { + const { valueDeclaration } = symbol; + if (!valueDeclaration || + !(node.flags & NodeFlags.Ambient && !(valueDeclaration.flags & NodeFlags.Ambient)) && + (isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node)) || + (valueDeclaration.kind !== node.kind && isEffectiveModuleDeclaration(valueDeclaration))) { + // other kinds of value declarations take precedence over modules and assignment declarations + symbol.valueDeclaration = node; + } + } + export function isFunctionSymbol(symbol: Symbol | undefined) { if (!symbol || !symbol.valueDeclaration) { return false; diff --git a/tests/baselines/reference/jsDeclarationsClassExtendsVisibility.symbols b/tests/baselines/reference/jsDeclarationsClassExtendsVisibility.symbols index cbf083c36f..a59d302e7f 100644 --- a/tests/baselines/reference/jsDeclarationsClassExtendsVisibility.symbols +++ b/tests/baselines/reference/jsDeclarationsClassExtendsVisibility.symbols @@ -25,7 +25,7 @@ module.exports = Foo; >Foo : Symbol(Foo, Decl(cls.js, 4, 2)) module.exports.Strings = Strings; ->module.exports.Strings : Symbol(Strings) +>module.exports.Strings : Symbol(Strings, Decl(cls.js, 6, 21)) >module.exports : Symbol(Strings, Decl(cls.js, 6, 21)) >module : Symbol(module, Decl(cls.js, 5, 24)) >exports : Symbol("tests/cases/conformance/jsdoc/declarations/cls", Decl(cls.js, 0, 0)) diff --git a/tests/baselines/reference/jsDeclarationsClassStatic.symbols b/tests/baselines/reference/jsDeclarationsClassStatic.symbols index d79d144e50..8687099bbe 100644 --- a/tests/baselines/reference/jsDeclarationsClassStatic.symbols +++ b/tests/baselines/reference/jsDeclarationsClassStatic.symbols @@ -34,7 +34,7 @@ module.exports = Handler; >Handler : Symbol(Handler, Decl(source.js, 0, 0), Decl(source.js, 7, 1)) module.exports.Strings = Strings ->module.exports.Strings : Symbol(Strings) +>module.exports.Strings : Symbol(Strings, Decl(source.js, 14, 25)) >module.exports : Symbol(Strings, Decl(source.js, 14, 25)) >module : Symbol(module, Decl(source.js, 12, 1)) >exports : Symbol("tests/cases/conformance/jsdoc/declarations/source", Decl(source.js, 0, 0)) diff --git a/tests/baselines/reference/jsDeclarationsCrossfileMerge.symbols b/tests/baselines/reference/jsDeclarationsCrossfileMerge.symbols index 99455853c4..64753e212b 100644 --- a/tests/baselines/reference/jsDeclarationsCrossfileMerge.symbols +++ b/tests/baselines/reference/jsDeclarationsCrossfileMerge.symbols @@ -13,7 +13,7 @@ module.exports = m.default; >default : Symbol(default, Decl(exporter.js, 0, 22)) module.exports.memberName = "thing"; ->module.exports.memberName : Symbol(memberName) +>module.exports.memberName : Symbol(memberName, Decl(index.js, 2, 27)) >module.exports : Symbol(memberName, Decl(index.js, 2, 27)) >module : Symbol(module, Decl(index.js, 0, 32)) >exports : Symbol("tests/cases/conformance/jsdoc/declarations/index", Decl(index.js, 0, 0)) diff --git a/tests/baselines/reference/jsDeclarationsExportAssignedClassExpressionAnonymousWithSub.symbols b/tests/baselines/reference/jsDeclarationsExportAssignedClassExpressionAnonymousWithSub.symbols index 228bb10ccc..aa5df49712 100644 --- a/tests/baselines/reference/jsDeclarationsExportAssignedClassExpressionAnonymousWithSub.symbols +++ b/tests/baselines/reference/jsDeclarationsExportAssignedClassExpressionAnonymousWithSub.symbols @@ -18,7 +18,7 @@ module.exports = class { } } module.exports.Sub = class { ->module.exports.Sub : Symbol(Sub) +>module.exports.Sub : Symbol(Sub, Decl(index.js, 7, 1)) >module.exports : Symbol(Sub, Decl(index.js, 7, 1)) >module : Symbol(module, Decl(index.js, 0, 0), Decl(index.js, 10, 27)) >exports : Symbol("tests/cases/conformance/jsdoc/declarations/index", Decl(index.js, 0, 0)) diff --git a/tests/baselines/reference/jsDeclarationsExportAssignedClassExpressionShadowing.symbols b/tests/baselines/reference/jsDeclarationsExportAssignedClassExpressionShadowing.symbols index 40a6fb5614..a911c06ee2 100644 --- a/tests/baselines/reference/jsDeclarationsExportAssignedClassExpressionShadowing.symbols +++ b/tests/baselines/reference/jsDeclarationsExportAssignedClassExpressionShadowing.symbols @@ -27,7 +27,7 @@ module.exports = class Q { } } module.exports.Another = Q; ->module.exports.Another : Symbol(Another) +>module.exports.Another : Symbol(Another, Decl(index.js, 10, 1)) >module.exports : Symbol(Another, Decl(index.js, 10, 1)) >module : Symbol(module, Decl(index.js, 5, 1)) >exports : Symbol("tests/cases/conformance/jsdoc/declarations/index", Decl(index.js, 0, 0)) diff --git a/tests/baselines/reference/jsDeclarationsExportSubAssignments.symbols b/tests/baselines/reference/jsDeclarationsExportSubAssignments.symbols index a764b8e99d..5939221ce9 100644 --- a/tests/baselines/reference/jsDeclarationsExportSubAssignments.symbols +++ b/tests/baselines/reference/jsDeclarationsExportSubAssignments.symbols @@ -19,7 +19,7 @@ module.exports = Foo; >Foo : Symbol(Foo, Decl(cls.js, 3, 2)) module.exports.Strings = Strings; ->module.exports.Strings : Symbol(Strings) +>module.exports.Strings : Symbol(Strings, Decl(cls.js, 5, 21)) >module.exports : Symbol(Strings, Decl(cls.js, 5, 21)) >module : Symbol(module, Decl(cls.js, 4, 12)) >exports : Symbol("tests/cases/conformance/jsdoc/declarations/cls", Decl(cls.js, 0, 0)) diff --git a/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation.symbols b/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation.symbols new file mode 100644 index 0000000000..e11817cab0 --- /dev/null +++ b/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation.symbols @@ -0,0 +1,43 @@ +=== /test.js === +class Abcde { +>Abcde : Symbol(Abcde, Decl(test.js, 0, 0)) + + /** @type {string} */ + x; +>x : Symbol(Abcde.x, Decl(test.js, 0, 13)) +} + +module.exports = { +>module.exports : Symbol("/test", Decl(test.js, 0, 0)) +>module : Symbol("/test.js", Decl(test.js, 3, 1), Decl(index.ts, 0, 31)) +>exports : Symbol("/test.js", Decl(test.js, 3, 1), Decl(index.ts, 0, 31)) + + Abcde +>Abcde : Symbol(Abcde, Decl(test.js, 5, 18)) + +}; + +=== /index.ts === +import { Abcde } from "./test"; +>Abcde : Symbol(Abcde, Decl(index.ts, 0, 8)) + +declare module "./test" { +>"./test" : Symbol("/test.js", Decl(test.js, 3, 1), Decl(index.ts, 0, 31)) + + interface Abcde { b: string } +>Abcde : Symbol(Abcde, Decl(index.ts, 2, 25), Decl(test.js, 5, 18)) +>b : Symbol(Abcde.b, Decl(index.ts, 3, 19)) +} + +new Abcde().x; +>new Abcde().x : Symbol(Abcde.x, Decl(test.js, 0, 13)) +>Abcde : Symbol(Abcde, Decl(index.ts, 0, 8)) +>x : Symbol(Abcde.x, Decl(test.js, 0, 13)) + +// Bug: the type meaning from /test.js does not +// propagate through the object literal export. +const x: Abcde = { b: "" }; +>x : Symbol(x, Decl(index.ts, 10, 5)) +>Abcde : Symbol(Abcde, Decl(index.ts, 0, 8)) +>b : Symbol(b, Decl(index.ts, 10, 18)) + diff --git a/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation.types b/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation.types new file mode 100644 index 0000000000..e53d62ff17 --- /dev/null +++ b/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation.types @@ -0,0 +1,46 @@ +=== /test.js === +class Abcde { +>Abcde : Abcde + + /** @type {string} */ + x; +>x : string +} + +module.exports = { +>module.exports = { Abcde} : { Abcde: typeof Abcde; } +>module.exports : { Abcde: typeof Abcde; } +>module : { "\"/test\"": { Abcde: typeof Abcde; }; } +>exports : { Abcde: typeof Abcde; } +>{ Abcde} : { Abcde: typeof Abcde; } + + Abcde +>Abcde : typeof Abcde + +}; + +=== /index.ts === +import { Abcde } from "./test"; +>Abcde : typeof Abcde + +declare module "./test" { +>"./test" : { Abcde: typeof Abcde; } + + interface Abcde { b: string } +>b : string +} + +new Abcde().x; +>new Abcde().x : string +>new Abcde() : Abcde +>Abcde : typeof Abcde +>x : string + +// Bug: the type meaning from /test.js does not +// propagate through the object literal export. +const x: Abcde = { b: "" }; +>x : Abcde +>{ b: "" } : { b: string; } +>b : string +>"" : "" + diff --git a/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.errors.txt b/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.errors.txt new file mode 100644 index 0000000000..44a0efae0d --- /dev/null +++ b/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.errors.txt @@ -0,0 +1,27 @@ +/index.ts(4,16): error TS2300: Duplicate identifier 'a'. +/index.ts(7,3): error TS2339: Property 'toFixed' does not exist on type 'string'. +/test.js(2,3): error TS2300: Duplicate identifier 'a'. + + +==== /test.js (1 errors) ==== + module.exports = { + a: "ok" + ~ +!!! error TS2300: Duplicate identifier 'a'. +!!! related TS6203 /index.ts:4:16: 'a' was also declared here. + }; + +==== /index.ts (2 errors) ==== + import { a } from "./test"; + + declare module "./test" { + export const a: number; + ~ +!!! error TS2300: Duplicate identifier 'a'. +!!! related TS6203 /test.js:2:3: 'a' was also declared here. + } + + a.toFixed(); + ~~~~~~~ +!!! error TS2339: Property 'toFixed' does not exist on type 'string'. + \ No newline at end of file diff --git a/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.symbols b/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.symbols new file mode 100644 index 0000000000..02da41d876 --- /dev/null +++ b/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.symbols @@ -0,0 +1,25 @@ +=== /test.js === +module.exports = { +>module.exports : Symbol("/test", Decl(test.js, 0, 0)) +>module : Symbol("/test.js", Decl(test.js, 0, 0), Decl(index.ts, 0, 27)) +>exports : Symbol("/test.js", Decl(test.js, 0, 0), Decl(index.ts, 0, 27)) + + a: "ok" +>a : Symbol(a, Decl(test.js, 0, 18)) + +}; + +=== /index.ts === +import { a } from "./test"; +>a : Symbol(a, Decl(index.ts, 0, 8)) + +declare module "./test" { +>"./test" : Symbol("/test.js", Decl(test.js, 0, 0), Decl(index.ts, 0, 27)) + + export const a: number; +>a : Symbol(a, Decl(index.ts, 3, 14)) +} + +a.toFixed(); +>a : Symbol(a, Decl(index.ts, 0, 8)) + diff --git a/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.types b/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.types new file mode 100644 index 0000000000..fa6e940f60 --- /dev/null +++ b/tests/baselines/reference/jsExportMemberMergedWithModuleAugmentation2.types @@ -0,0 +1,31 @@ +=== /test.js === +module.exports = { +>module.exports = { a: "ok"} : { a: string | number; } +>module.exports : { a: string | number; } +>module : { "\"/test\"": { a: string | number; }; } +>exports : { a: string | number; } +>{ a: "ok"} : { a: string; } + + a: "ok" +>a : string +>"ok" : "ok" + +}; + +=== /index.ts === +import { a } from "./test"; +>a : string + +declare module "./test" { +>"./test" : { a: string | number; } + + export const a: number; +>a : number +} + +a.toFixed(); +>a.toFixed() : any +>a.toFixed : any +>a : string +>toFixed : any + diff --git a/tests/baselines/reference/moduleExportAlias2.symbols b/tests/baselines/reference/moduleExportAlias2.symbols index f6ebc31531..6dc88d4d8c 100644 --- a/tests/baselines/reference/moduleExportAlias2.symbols +++ b/tests/baselines/reference/moduleExportAlias2.symbols @@ -37,7 +37,7 @@ exports = module.exports = C >C : Symbol(C, Decl(semver.js, 2, 22)) exports.f = n => n + 1 ->exports.f : Symbol(f) +>exports.f : Symbol(f, Decl(semver.js, 1, 28)) >exports : Symbol(f, Decl(semver.js, 1, 28)) >f : Symbol(f, Decl(semver.js, 1, 28)) >n : Symbol(n, Decl(semver.js, 2, 11)) diff --git a/tests/baselines/reference/moduleExportAlias4.symbols b/tests/baselines/reference/moduleExportAlias4.symbols index a3e2e2d593..fd149513b7 100644 --- a/tests/baselines/reference/moduleExportAlias4.symbols +++ b/tests/baselines/reference/moduleExportAlias4.symbols @@ -12,7 +12,7 @@ module.exports = class C {} >C : Symbol(C, Decl(bug24024.js, 2, 16)) module.exports.D = class D { } ->module.exports.D : Symbol(D) +>module.exports.D : Symbol(D, Decl(bug24024.js, 2, 27)) >module.exports : Symbol(D, Decl(bug24024.js, 2, 27)) >module : Symbol(module, Decl(bug24024.js, 1, 31)) >exports : Symbol("tests/cases/conformance/salsa/bug24024", Decl(bug24024.js, 0, 0)) diff --git a/tests/baselines/reference/moduleExportAliasImported.symbols b/tests/baselines/reference/moduleExportAliasImported.symbols index 9649cc0a3b..298e439a0d 100644 --- a/tests/baselines/reference/moduleExportAliasImported.symbols +++ b/tests/baselines/reference/moduleExportAliasImported.symbols @@ -1,6 +1,6 @@ === tests/cases/conformance/salsa/bug28014.js === exports.version = 1 ->exports.version : Symbol(version) +>exports.version : Symbol(version, Decl(bug28014.js, 0, 0)) >exports : Symbol(version, Decl(bug28014.js, 0, 0)) >version : Symbol(version, Decl(bug28014.js, 0, 0)) diff --git a/tests/baselines/reference/moduleExportPropertyAssignmentDefault.symbols b/tests/baselines/reference/moduleExportPropertyAssignmentDefault.symbols index 8e704e5fe0..0645e00f00 100644 --- a/tests/baselines/reference/moduleExportPropertyAssignmentDefault.symbols +++ b/tests/baselines/reference/moduleExportPropertyAssignmentDefault.symbols @@ -9,7 +9,7 @@ module.exports = axios // both assignments should be ok >axios : Symbol(axios, Decl(axios.js, 0, 3)) module.exports.default = axios ->module.exports.default : Symbol(default) +>module.exports.default : Symbol(default, Decl(axios.js, 1, 22)) >module.exports : Symbol(default, Decl(axios.js, 1, 22)) >module : Symbol(module, Decl(axios.js, 0, 14)) >exports : Symbol("tests/cases/conformance/salsa/axios", Decl(axios.js, 0, 0)) diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.symbols b/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.symbols index 3ee790d768..1c4aa535f6 100644 --- a/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.symbols +++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment3.symbols @@ -13,14 +13,14 @@ mod1.justExport.toFixed() >toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) mod1.bothBefore.toFixed() // error, 'toFixed' not on 'string | number' ->mod1.bothBefore : Symbol(bothBefore) +>mod1.bothBefore : Symbol(bothBefore, Decl(mod1.js, 3, 18), Decl(mod1.js, 0, 0)) >mod1 : Symbol(mod1, Decl(a.js, 1, 3)) ->bothBefore : Symbol(bothBefore) +>bothBefore : Symbol(bothBefore, Decl(mod1.js, 3, 18), Decl(mod1.js, 0, 0)) mod1.bothAfter.toFixed() // error, 'toFixed' not on 'string | number' ->mod1.bothAfter : Symbol(bothAfter) +>mod1.bothAfter : Symbol(bothAfter, Decl(mod1.js, 4, 18), Decl(mod1.js, 6, 1)) >mod1 : Symbol(mod1, Decl(a.js, 1, 3)) ->bothAfter : Symbol(bothAfter) +>bothAfter : Symbol(bothAfter, Decl(mod1.js, 4, 18), Decl(mod1.js, 6, 1)) mod1.justProperty.length >mod1.justProperty.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) @@ -41,7 +41,7 @@ declare function require(name: string): any; === tests/cases/conformance/salsa/mod1.js === /// module.exports.bothBefore = 'string' ->module.exports.bothBefore : Symbol(bothBefore) +>module.exports.bothBefore : Symbol(bothBefore, Decl(mod1.js, 3, 18), Decl(mod1.js, 0, 0)) >module.exports : Symbol(bothBefore, Decl(mod1.js, 0, 0)) >module : Symbol(module, Decl(mod1.js, 0, 0)) >exports : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0)) @@ -62,7 +62,7 @@ module.exports = { >bothAfter : Symbol(bothAfter, Decl(mod1.js, 4, 18)) } module.exports.bothAfter = 'string' ->module.exports.bothAfter : Symbol(bothAfter) +>module.exports.bothAfter : Symbol(bothAfter, Decl(mod1.js, 4, 18), Decl(mod1.js, 6, 1)) >module.exports : Symbol(bothAfter, Decl(mod1.js, 6, 1)) >module : Symbol(module, Decl(mod1.js, 0, 0)) >exports : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0)) diff --git a/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.symbols b/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.symbols index ad04485687..76fecbc0b2 100644 --- a/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.symbols +++ b/tests/baselines/reference/moduleExportWithExportPropertyAssignment4.symbols @@ -41,7 +41,7 @@ declare function require(name: string): any; === tests/cases/conformance/salsa/mod1.js === /// module.exports.bothBefore = 'string' ->module.exports.bothBefore : Symbol(bothBefore) +>module.exports.bothBefore : Symbol(bothBefore, Decl(mod1.js, 2, 16), Decl(mod1.js, 0, 0)) >module.exports : Symbol(bothBefore, Decl(mod1.js, 2, 16), Decl(mod1.js, 0, 0)) >module : Symbol(module, Decl(mod1.js, 0, 0)) >exports : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0)) @@ -75,14 +75,14 @@ function A() { >p : Symbol(A.p, Decl(mod1.js, 6, 14)) } module.exports.bothAfter = 'string' ->module.exports.bothAfter : Symbol(bothAfter) +>module.exports.bothAfter : Symbol(bothAfter, Decl(mod1.js, 3, 16), Decl(mod1.js, 8, 1)) >module.exports : Symbol(bothAfter, Decl(mod1.js, 3, 16), Decl(mod1.js, 8, 1)) >module : Symbol(module, Decl(mod1.js, 0, 0)) >exports : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0)) >bothAfter : Symbol(bothAfter, Decl(mod1.js, 3, 16), Decl(mod1.js, 8, 1)) module.exports.justProperty = 'string' ->module.exports.justProperty : Symbol(justProperty) +>module.exports.justProperty : Symbol(justProperty, Decl(mod1.js, 9, 35)) >module.exports : Symbol(justProperty, Decl(mod1.js, 9, 35)) >module : Symbol(module, Decl(mod1.js, 0, 0)) >exports : Symbol("tests/cases/conformance/salsa/mod1", Decl(mod1.js, 0, 0)) diff --git a/tests/baselines/reference/typedefCrossModule2.types b/tests/baselines/reference/typedefCrossModule2.types index c1afae93d8..9a1b0922f9 100644 --- a/tests/baselines/reference/typedefCrossModule2.types +++ b/tests/baselines/reference/typedefCrossModule2.types @@ -1,7 +1,7 @@ === tests/cases/conformance/jsdoc/use.js === var mod = require('./mod1.js'); ->mod : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; } ->require('./mod1.js') : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; } +>mod : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; } +>require('./mod1.js') : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; } >require : any >'./mod1.js' : "./mod1.js" @@ -17,7 +17,7 @@ var bbb = new mod.Baz(); >bbb : any >new mod.Baz() : any >mod.Baz : any ->mod : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; } +>mod : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; } >Baz : any === tests/cases/conformance/jsdoc/mod1.js === @@ -31,16 +31,16 @@ class Foo { } // should error exports.Bar = class { } >exports.Bar = class { } : typeof Bar >exports.Bar : typeof Bar ->exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; } +>exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; } >Bar : typeof Bar >class { } : typeof Bar /** @typedef {number} Baz */ module.exports = { ->module.exports = { Baz: class { }} : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; } ->module.exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; } ->module : { "\"tests/cases/conformance/jsdoc/mod1\"": { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }; } ->exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; } +>module.exports = { Baz: class { }} : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; } +>module.exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; } +>module : { "\"tests/cases/conformance/jsdoc/mod1\"": { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }; } +>exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; } >{ Baz: class { }} : { Baz: typeof Baz; } Baz: class { } @@ -59,16 +59,16 @@ var Qux = 2; exports.Quid = 2; >exports.Quid = 2 : 2 >exports.Quid : number ->exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; } +>exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; } >Quid : number >2 : 2 /** @typedef {number} Quack */ module.exports = { ->module.exports = { Quack: 2} : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; } ->module.exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; } ->module : { "\"tests/cases/conformance/jsdoc/mod1\"": { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; }; } ->exports : { Baz: any; Bar: typeof Bar; Quid: number; } | { Quack: any; Bar: typeof Bar; Quid: number; } +>module.exports = { Quack: 2} : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; } +>module.exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; } +>module : { "\"tests/cases/conformance/jsdoc/mod1\"": { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; }; } +>exports : { Baz: typeof Baz; Bar: typeof Bar; Quid: number; } | { Quack: number; Bar: typeof Bar; Quid: number; } >{ Quack: 2} : { Quack: number; } Quack: 2 diff --git a/tests/baselines/reference/typedefCrossModule4.types b/tests/baselines/reference/typedefCrossModule4.types index 61c2dace77..eb01fe9562 100644 --- a/tests/baselines/reference/typedefCrossModule4.types +++ b/tests/baselines/reference/typedefCrossModule4.types @@ -4,10 +4,10 @@ class Bar { } >Bar : Bar module.exports = { Foo: Bar }; ->module.exports = { Foo: Bar } : { Foo: any; } ->module.exports : { Foo: any; } ->module : { "\"tests/cases/conformance/jsdoc/mod3\"": { Foo: any; }; } ->exports : { Foo: any; } +>module.exports = { Foo: Bar } : { Foo: typeof Bar; } +>module.exports : { Foo: typeof Bar; } +>module : { "\"tests/cases/conformance/jsdoc/mod3\"": { Foo: typeof Bar; }; } +>exports : { Foo: typeof Bar; } >{ Foo: Bar } : { Foo: typeof Bar; } >Foo : typeof Bar >Bar : typeof Bar diff --git a/tests/cases/compiler/jsExportMemberMergedWithModuleAugmentation.ts b/tests/cases/compiler/jsExportMemberMergedWithModuleAugmentation.ts new file mode 100644 index 0000000000..1c129be364 --- /dev/null +++ b/tests/cases/compiler/jsExportMemberMergedWithModuleAugmentation.ts @@ -0,0 +1,26 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /test.js +class Abcde { + /** @type {string} */ + x; +} + +module.exports = { + Abcde +}; + +// @Filename: /index.ts +import { Abcde } from "./test"; + +declare module "./test" { + interface Abcde { b: string } +} + +new Abcde().x; + +// Bug: the type meaning from /test.js does not +// propagate through the object literal export. +const x: Abcde = { b: "" }; diff --git a/tests/cases/compiler/jsExportMemberMergedWithModuleAugmentation2.ts b/tests/cases/compiler/jsExportMemberMergedWithModuleAugmentation2.ts new file mode 100644 index 0000000000..7ef22ee7ec --- /dev/null +++ b/tests/cases/compiler/jsExportMemberMergedWithModuleAugmentation2.ts @@ -0,0 +1,17 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /test.js +module.exports = { + a: "ok" +}; + +// @Filename: /index.ts +import { a } from "./test"; + +declare module "./test" { + export const a: number; +} + +a.toFixed(); diff --git a/tests/cases/fourslash/completionsImportModuleAugmentationWithJS.ts b/tests/cases/fourslash/completionsImportModuleAugmentationWithJS.ts new file mode 100644 index 0000000000..8ea84247d8 --- /dev/null +++ b/tests/cases/fourslash/completionsImportModuleAugmentationWithJS.ts @@ -0,0 +1,41 @@ +// #37833 + +/// + +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /test.js +////class Abcde { +//// x +////} +//// +////module.exports = { +//// Abcde +////}; + +// @Filename: /index.ts +////export {}; +////declare module "./test" { +//// interface Abcde { b: string } +////} +//// +////Abcde/**/ + +verify.applyCodeActionFromCompletion("", { + name: "Abcde", + source: "/test", + description: `Import 'Abcde' from module "./test"`, + newFileContent: `import { Abcde } from "./test"; + +export {}; +declare module "./test" { + interface Abcde { b: string } +} + +Abcde`, + preferences: { + includeCompletionsForModuleExports: true + } +});