From 95dc1f25745f343b9cfdc854a38cd4bf1d1aa2b9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 8 Oct 2018 08:53:21 -0700 Subject: [PATCH] Fix name resolution of `exports` in JS (#27394) The ad-hoc name resolution rule for `exports` forgets to check the requested meaning. When `getTypeReferenceType` calls` resolveTypeReferenceName` with `Type` only in order to give an error when the program uses a value like a type, it is incorrectly able to resolve `exports` instead of producing an error. Then this incorrect symbol gets treated like an alias, which it isn't, causing the assert. The fix, for now, is to make resolution of `exports` check the requested meaning so that it only resolves when `Value` is requested. This makes the above code an error ("Cannot use the namespace 'exports' as a type."), but I think this is fine for a bug fix. We can decide later if `exports` should behave like other expandos and be a legal type reference. Note that the name actually does resolve correctly, so JS users will get the desired completions. They'll just have an error to suppress if they have checkJs on. --- src/compiler/checker.ts | 2 +- .../jsdocTypeReferenceExports.errors.txt | 13 +++++++++++++ .../reference/jsdocTypeReferenceExports.symbols | 13 +++++++++++++ .../reference/jsdocTypeReferenceExports.types | 15 +++++++++++++++ .../jsdoc/jsdocTypeReferenceExports.ts | 10 ++++++++++ 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/jsdocTypeReferenceExports.errors.txt create mode 100644 tests/baselines/reference/jsdocTypeReferenceExports.symbols create mode 100644 tests/baselines/reference/jsdocTypeReferenceExports.types create mode 100644 tests/cases/conformance/jsdoc/jsdocTypeReferenceExports.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 46428a16fde..cb18cd2be04 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1456,7 +1456,7 @@ namespace ts { if (!result) { if (lastLocation) { Debug.assert(lastLocation.kind === SyntaxKind.SourceFile); - if ((lastLocation as SourceFile).commonJsModuleIndicator && name === "exports") { + if ((lastLocation as SourceFile).commonJsModuleIndicator && name === "exports" && meaning & lastLocation.symbol.flags) { return lastLocation.symbol; } } diff --git a/tests/baselines/reference/jsdocTypeReferenceExports.errors.txt b/tests/baselines/reference/jsdocTypeReferenceExports.errors.txt new file mode 100644 index 00000000000..48d0321be71 --- /dev/null +++ b/tests/baselines/reference/jsdocTypeReferenceExports.errors.txt @@ -0,0 +1,13 @@ +tests/cases/conformance/jsdoc/bug27342.js(3,11): error TS2709: Cannot use namespace 'exports' as a type. + + +==== tests/cases/conformance/jsdoc/bug27342.js (1 errors) ==== + module.exports = {} + /** + * @type {exports} + ~~~~~~~ +!!! error TS2709: Cannot use namespace 'exports' as a type. + */ + var x + + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocTypeReferenceExports.symbols b/tests/baselines/reference/jsdocTypeReferenceExports.symbols new file mode 100644 index 00000000000..0ae0a5e73a7 --- /dev/null +++ b/tests/baselines/reference/jsdocTypeReferenceExports.symbols @@ -0,0 +1,13 @@ +=== tests/cases/conformance/jsdoc/bug27342.js === +module.exports = {} +>module.exports : Symbol("tests/cases/conformance/jsdoc/bug27342", Decl(bug27342.js, 0, 0)) +>module : Symbol(module, Decl(bug27342.js, 0, 0)) +>exports : Symbol("tests/cases/conformance/jsdoc/bug27342", Decl(bug27342.js, 0, 0)) + +/** + * @type {exports} + */ +var x +>x : Symbol(x, Decl(bug27342.js, 4, 3)) + + diff --git a/tests/baselines/reference/jsdocTypeReferenceExports.types b/tests/baselines/reference/jsdocTypeReferenceExports.types new file mode 100644 index 00000000000..843bb08f91b --- /dev/null +++ b/tests/baselines/reference/jsdocTypeReferenceExports.types @@ -0,0 +1,15 @@ +=== tests/cases/conformance/jsdoc/bug27342.js === +module.exports = {} +>module.exports = {} : typeof import("tests/cases/conformance/jsdoc/bug27342") +>module.exports : typeof import("tests/cases/conformance/jsdoc/bug27342") +>module : { "tests/cases/conformance/jsdoc/bug27342": typeof import("tests/cases/conformance/jsdoc/bug27342"); } +>exports : typeof import("tests/cases/conformance/jsdoc/bug27342") +>{} : {} + +/** + * @type {exports} + */ +var x +>x : typeof import("tests/cases/conformance/jsdoc/bug27342") + + diff --git a/tests/cases/conformance/jsdoc/jsdocTypeReferenceExports.ts b/tests/cases/conformance/jsdoc/jsdocTypeReferenceExports.ts new file mode 100644 index 00000000000..e711ad4f58f --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTypeReferenceExports.ts @@ -0,0 +1,10 @@ +// @allowJs: true +// @noEmit: true +// @checkJs: true +// @Filename: bug27342.js +module.exports = {} +/** + * @type {exports} + */ +var x +