From f05e4a55a0240756415e15d3bebd7a0abfb7dd32 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Fri, 21 Jun 2024 23:21:18 +0300 Subject: [PATCH] fix(58955): support resolution-mode in jsdoc type imports --- 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 b7220fb89543d..2a6ae727613b2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4617,7 +4617,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)); + const overrideHost = findAncestor(location, l => isImportTypeNode(l) || isExportDeclaration(l) || isImportDeclaration(l) || isJSDocImportTag(l)); // 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 da5ae9592ccb2..08b6c447fd9de 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 72dd4f742a7f5..fb38a1c8f07bd 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -10942,7 +10942,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; +}