From 738bd605dfab2102cad7a8702ceae1ed2f55fbec Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:37:58 -0700 Subject: [PATCH] Cherry-pick #58966 to release-5.5 (#59002) Co-authored-by: Oleksandr T --- src/compiler/checker.ts | 2 +- src/compiler/program.ts | 2 +- src/compiler/utilities.ts | 2 +- .../reference/importTag17.errors.txt | 44 +++++++++++++++++++ tests/baselines/reference/importTag17.symbols | 32 ++++++++++++++ tests/baselines/reference/importTag17.types | 40 +++++++++++++++++ tests/cases/conformance/jsdoc/importTag17.ts | 40 +++++++++++++++++ 7 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/importTag17.errors.txt create mode 100644 tests/baselines/reference/importTag17.symbols create mode 100644 tests/baselines/reference/importTag17.types create mode 100644 tests/cases/conformance/jsdoc/importTag17.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 57cca12c38c29..1db88a48abcab 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4590,7 +4590,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (moduleResolutionKind === ModuleResolutionKind.Node16 || moduleResolutionKind === ModuleResolutionKind.NodeNext) { const isSyncImport = (currentSourceFile.impliedNodeFormat === ModuleKind.CommonJS && !findAncestor(location, isImportCall)) || !!findAncestor(location, isImportEqualsDeclaration); - const overrideHost = findAncestor(location, l => isImportTypeNode(l) || isExportDeclaration(l) || isImportDeclaration(l)) as ImportTypeNode | ImportDeclaration | ExportDeclaration | undefined; + const overrideHost = findAncestor(location, l => isImportTypeNode(l) || isExportDeclaration(l) || isImportDeclaration(l) || isJSDocImportTag(l)) as ImportTypeNode | ImportDeclaration | ExportDeclaration | JSDocImportTag | undefined; // An override clause will take effect for type-only imports and import types, and allows importing the types across formats, regardless of // normal mode restrictions if (isSyncImport && sourceFile.impliedNodeFormat === ModuleKind.ESNext && !hasResolutionModeOverride(overrideHost)) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index e61a93bf871a2..023013b64b641 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -902,7 +902,7 @@ export function getModeForUsageLocation(file: { impliedNodeFormat?: ResolutionMo } function getModeForUsageLocationWorker(file: { impliedNodeFormat?: ResolutionMode; }, usage: StringLiteralLike, compilerOptions?: CompilerOptions) { - if ((isImportDeclaration(usage.parent) || isExportDeclaration(usage.parent))) { + if (isImportDeclaration(usage.parent) || isExportDeclaration(usage.parent) || isJSDocImportTag(usage.parent)) { const isTypeOnly = isExclusivelyTypeOnlyImportOrExport(usage.parent); if (isTypeOnly) { const override = getResolutionModeOverride(usage.parent.attributes); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 81cfddfc0578a..ad32f1903ec56 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -10850,7 +10850,7 @@ export function isExpandoPropertyDeclaration(declaration: Declaration | undefine } /** @internal */ -export function hasResolutionModeOverride(node: ImportTypeNode | ImportDeclaration | ExportDeclaration | undefined) { +export function hasResolutionModeOverride(node: ImportTypeNode | ImportDeclaration | ExportDeclaration | JSDocImportTag | undefined) { if (node === undefined) { return false; } diff --git a/tests/baselines/reference/importTag17.errors.txt b/tests/baselines/reference/importTag17.errors.txt new file mode 100644 index 0000000000000..126faa85d899d --- /dev/null +++ b/tests/baselines/reference/importTag17.errors.txt @@ -0,0 +1,44 @@ +/a.js(8,5): error TS2322: Type '1' is not assignable to type '"module"'. +/a.js(15,5): error TS2322: Type '1' is not assignable to type '"script"'. + + +==== /node_modules/@types/foo/package.json (0 errors) ==== + { + "name": "@types/foo", + "version": "1.0.0", + "exports": { + ".": { + "import": "./index.d.mts", + "require": "./index.d.cts" + } + } + } + +==== /node_modules/@types/foo/index.d.mts (0 errors) ==== + export declare const Import: "module"; + +==== /node_modules/@types/foo/index.d.cts (0 errors) ==== + export declare const Require: "script"; + +==== /a.js (2 errors) ==== + /** @import { Import } from 'foo' with { 'resolution-mode': 'import' } */ + /** @import { Require } from 'foo' with { 'resolution-mode': 'require' } */ + + /** + * @returns { Import } + */ + export function f1() { + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type '"module"'. + } + + /** + * @returns { Require } + */ + export function f2() { + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type '"script"'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/importTag17.symbols b/tests/baselines/reference/importTag17.symbols new file mode 100644 index 0000000000000..8cb42c771b67a --- /dev/null +++ b/tests/baselines/reference/importTag17.symbols @@ -0,0 +1,32 @@ +//// [tests/cases/conformance/jsdoc/importTag17.ts] //// + +=== /node_modules/@types/foo/index.d.mts === +export declare const Import: "module"; +>Import : Symbol(Import, Decl(index.d.mts, 0, 20)) + +=== /node_modules/@types/foo/index.d.cts === +export declare const Require: "script"; +>Require : Symbol(Require, Decl(index.d.cts, 0, 20)) + +=== /a.js === +/** @import { Import } from 'foo' with { 'resolution-mode': 'import' } */ +/** @import { Require } from 'foo' with { 'resolution-mode': 'require' } */ + +/** + * @returns { Import } + */ +export function f1() { +>f1 : Symbol(f1, Decl(a.js, 0, 0)) + + return 1; +} + +/** + * @returns { Require } + */ +export function f2() { +>f2 : Symbol(f2, Decl(a.js, 8, 1)) + + return 1; +} + diff --git a/tests/baselines/reference/importTag17.types b/tests/baselines/reference/importTag17.types new file mode 100644 index 0000000000000..7e30a956fa0f6 --- /dev/null +++ b/tests/baselines/reference/importTag17.types @@ -0,0 +1,40 @@ +//// [tests/cases/conformance/jsdoc/importTag17.ts] //// + +=== /node_modules/@types/foo/index.d.mts === +export declare const Import: "module"; +>Import : "module" +> : ^^^^^^^^ + +=== /node_modules/@types/foo/index.d.cts === +export declare const Require: "script"; +>Require : "script" +> : ^^^^^^^^ + +=== /a.js === +/** @import { Import } from 'foo' with { 'resolution-mode': 'import' } */ +/** @import { Require } from 'foo' with { 'resolution-mode': 'require' } */ + +/** + * @returns { Import } + */ +export function f1() { +>f1 : () => "module" +> : ^^^^^^^^^^^^^^ + + return 1; +>1 : 1 +> : ^ +} + +/** + * @returns { Require } + */ +export function f2() { +>f2 : () => "script" +> : ^^^^^^^^^^^^^^ + + return 1; +>1 : 1 +> : ^ +} + diff --git a/tests/cases/conformance/jsdoc/importTag17.ts b/tests/cases/conformance/jsdoc/importTag17.ts new file mode 100644 index 0000000000000..dd695504090e7 --- /dev/null +++ b/tests/cases/conformance/jsdoc/importTag17.ts @@ -0,0 +1,40 @@ +// @module: node16 +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @Filename: /node_modules/@types/foo/package.json +{ + "name": "@types/foo", + "version": "1.0.0", + "exports": { + ".": { + "import": "./index.d.mts", + "require": "./index.d.cts" + } + } +} + +// @Filename: /node_modules/@types/foo/index.d.mts +export declare const Import: "module"; + +// @Filename: /node_modules/@types/foo/index.d.cts +export declare const Require: "script"; + +// @Filename: /a.js +/** @import { Import } from 'foo' with { 'resolution-mode': 'import' } */ +/** @import { Require } from 'foo' with { 'resolution-mode': 'require' } */ + +/** + * @returns { Import } + */ +export function f1() { + return 1; +} + +/** + * @returns { Require } + */ +export function f2() { + return 1; +}