From f96578916528cf5b9b020595282994b204764432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 21 Oct 2026 09:30:51 +0200 Subject: [PATCH 1/3] Preserve type-only aliases when module resolution fails --- tsc/internal/checker/checker.go | 10 ++-- ...resolvedImport(module=commonjs).errors.txt | 40 +++++++++++++++ ...peOnlyUnresolvedImport(module=commonjs).js | 50 +++++++++++++++++++ ...yUnresolvedImport(module=commonjs).symbols | 48 ++++++++++++++++++ ...nlyUnresolvedImport(module=commonjs).types | 46 +++++++++++++++++ ...UnresolvedImport(module=esnext).errors.txt | 40 +++++++++++++++ ...TypeOnlyUnresolvedImport(module=esnext).js | 49 ++++++++++++++++++ ...nlyUnresolvedImport(module=esnext).symbols | 48 ++++++++++++++++++ ...eOnlyUnresolvedImport(module=esnext).types | 46 +++++++++++++++++ ...coratorMetadataTypeOnlyUnresolvedImport.ts | 21 ++++++++ 10 files changed, 393 insertions(+), 5 deletions(-) create mode 100644 tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).js create mode 100644 tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).symbols create mode 100644 tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).types create mode 100644 tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).js create mode 100644 tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).symbols create mode 100644 tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).types create mode 100644 tsc/testdata/tests/cases/compiler/decoratorMetadataTypeOnlyUnresolvedImport.ts diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 10ddb3c0c88b4..a85e78b47cf45 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -2200,6 +2200,8 @@ func isPropertyImmediatelyReferencedWithinDeclaration(declaration *ast.Node, usa func (c *Checker) getTypeOnlyAliasDeclaration(symbol *ast.Symbol) *ast.Node { if symbol.Flags()&ast.SymbolFlagsAlias != 0 { c.resolveAlias(symbol) + // Alias resolution may fail before recording the declaration's type-only marker. + c.markSymbolOfAliasDeclarationIfTypeOnly(core.FirstOrNil(symbol.Declarations()), nil) return c.aliasSymbolLinks.Get(symbol).typeOnlyDeclaration } return nil @@ -2209,12 +2211,10 @@ func (c *Checker) getTypeOnlyAliasDeclaration(symbol *ast.Symbol) *ast.Node { // the symbol for the given meaning func (c *Checker) getTypeOnlyAliasDeclarationEx(symbol *ast.Symbol, meaning ast.SymbolFlags) *ast.Node { for symbol.Flags()&ast.SymbolFlagsAlias != 0 && symbol.Flags()&meaning == 0 { - resolved := c.resolveAlias(symbol) - links := c.aliasSymbolLinks.Get(symbol) - if links.typeOnlyDeclaration != nil { - return links.typeOnlyDeclaration + if declaration := c.getTypeOnlyAliasDeclaration(symbol); declaration != nil { + return declaration } - symbol = resolved + symbol = c.resolveAlias(symbol) } return nil } diff --git a/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).errors.txt b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).errors.txt new file mode 100644 index 0000000000000..155ae7509088c --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).errors.txt @@ -0,0 +1,40 @@ +decoratorMetadataTypeOnlyUnresolvedImport.ts(1,22): error TS2307: Cannot find module './missing-default' or its corresponding type declarations. +decoratorMetadataTypeOnlyUnresolvedImport.ts(2,26): error TS2307: Cannot find module './missing-named' or its corresponding type declarations. +decoratorMetadataTypeOnlyUnresolvedImport.ts(3,37): error TS2307: Cannot find module './missing-default-specifier' or its corresponding type declarations. +decoratorMetadataTypeOnlyUnresolvedImport.ts(13,11): error TS1361: 'Foo' cannot be used as a value because it was imported using 'import type'. +decoratorMetadataTypeOnlyUnresolvedImport.ts(14,11): error TS1361: 'Bar' cannot be used as a value because it was imported using 'import type'. +decoratorMetadataTypeOnlyUnresolvedImport.ts(15,11): error TS1361: 'Baz' cannot be used as a value because it was imported using 'import type'. + + +==== decoratorMetadataTypeOnlyUnresolvedImport.ts (6 errors) ==== + import type Foo from "./missing-default"; + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2307: Cannot find module './missing-default' or its corresponding type declarations. + import { type Bar } from "./missing-named"; + ~~~~~~~~~~~~~~~~~ +!!! error TS2307: Cannot find module './missing-named' or its corresponding type declarations. + import type { default as Baz } from "./missing-default-specifier"; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2307: Cannot find module './missing-default-specifier' or its corresponding type declarations. + + function dec(_target: unknown, _key: unknown) {} + + class C { + @dec a!: Foo; + @dec b!: Bar; + @dec c!: Baz; + } + + const x = Foo; + ~~~ +!!! error TS1361: 'Foo' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 decoratorMetadataTypeOnlyUnresolvedImport.ts:1:8: 'Foo' was imported here. + const y = Bar; + ~~~ +!!! error TS1361: 'Bar' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 decoratorMetadataTypeOnlyUnresolvedImport.ts:2:10: 'Bar' was imported here. + const z = Baz; + ~~~ +!!! error TS1361: 'Baz' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 decoratorMetadataTypeOnlyUnresolvedImport.ts:3:15: 'Baz' was imported here. + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).js b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).js new file mode 100644 index 0000000000000..94f8d3bee0d29 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).js @@ -0,0 +1,50 @@ +//// [tests/cases/compiler/decoratorMetadataTypeOnlyUnresolvedImport.ts] //// + +//// [decoratorMetadataTypeOnlyUnresolvedImport.ts] +import type Foo from "./missing-default"; +import { type Bar } from "./missing-named"; +import type { default as Baz } from "./missing-default-specifier"; + +function dec(_target: unknown, _key: unknown) {} + +class C { + @dec a!: Foo; + @dec b!: Bar; + @dec c!: Baz; +} + +const x = Foo; +const y = Bar; +const z = Baz; + + +//// [decoratorMetadataTypeOnlyUnresolvedImport.js] +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +function dec(_target, _key) { } +class C { +} +__decorate([ + dec, + __metadata("design:type", Object) +], C.prototype, "a", void 0); +__decorate([ + dec, + __metadata("design:type", Object) +], C.prototype, "b", void 0); +__decorate([ + dec, + __metadata("design:type", Object) +], C.prototype, "c", void 0); +const x = Foo; +const y = Bar; +const z = Baz; diff --git a/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).symbols b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).symbols new file mode 100644 index 0000000000000..9281d089b8443 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).symbols @@ -0,0 +1,48 @@ +//// [tests/cases/compiler/decoratorMetadataTypeOnlyUnresolvedImport.ts] //// + +=== decoratorMetadataTypeOnlyUnresolvedImport.ts === +import type Foo from "./missing-default"; +>Foo : Symbol(Foo, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 0, 6)) + +import { type Bar } from "./missing-named"; +>Bar : Symbol(Bar, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 1, 8)) + +import type { default as Baz } from "./missing-default-specifier"; +>Baz : Symbol(Baz, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 13)) + +function dec(_target: unknown, _key: unknown) {} +>dec : Symbol(dec, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 66)) +>_target : Symbol(_target, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 4, 13)) +>_key : Symbol(_key, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 4, 30)) + +class C { +>C : Symbol(C, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 4, 48)) + + @dec a!: Foo; +>dec : Symbol(dec, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 66)) +>a : Symbol(C.a, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 6, 9)) +>Foo : Symbol(Foo, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 0, 6)) + + @dec b!: Bar; +>dec : Symbol(dec, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 66)) +>b : Symbol(C.b, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 7, 17)) +>Bar : Symbol(Bar, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 1, 8)) + + @dec c!: Baz; +>dec : Symbol(dec, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 66)) +>c : Symbol(C.c, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 8, 17)) +>Baz : Symbol(Baz, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 13)) +} + +const x = Foo; +>x : Symbol(x, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 12, 5)) +>Foo : Symbol(Foo, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 0, 6)) + +const y = Bar; +>y : Symbol(y, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 13, 5)) +>Bar : Symbol(Bar, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 1, 8)) + +const z = Baz; +>z : Symbol(z, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 14, 5)) +>Baz : Symbol(Baz, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 13)) + diff --git a/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).types b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).types new file mode 100644 index 0000000000000..8264a6bd572ac --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=commonjs).types @@ -0,0 +1,46 @@ +//// [tests/cases/compiler/decoratorMetadataTypeOnlyUnresolvedImport.ts] //// + +=== decoratorMetadataTypeOnlyUnresolvedImport.ts === +import type Foo from "./missing-default"; +>Foo : any + +import { type Bar } from "./missing-named"; +>Bar : any + +import type { default as Baz } from "./missing-default-specifier"; +>default : any +>Baz : any + +function dec(_target: unknown, _key: unknown) {} +>dec : (_target: unknown, _key: unknown) => void +>_target : unknown +>_key : unknown + +class C { +>C : C + + @dec a!: Foo; +>dec : (_target: unknown, _key: unknown) => void +>a : Foo + + @dec b!: Bar; +>dec : (_target: unknown, _key: unknown) => void +>b : Bar + + @dec c!: Baz; +>dec : (_target: unknown, _key: unknown) => void +>c : Baz +} + +const x = Foo; +>x : any +>Foo : any + +const y = Bar; +>y : any +>Bar : any + +const z = Baz; +>z : any +>Baz : any + diff --git a/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).errors.txt b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).errors.txt new file mode 100644 index 0000000000000..155ae7509088c --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).errors.txt @@ -0,0 +1,40 @@ +decoratorMetadataTypeOnlyUnresolvedImport.ts(1,22): error TS2307: Cannot find module './missing-default' or its corresponding type declarations. +decoratorMetadataTypeOnlyUnresolvedImport.ts(2,26): error TS2307: Cannot find module './missing-named' or its corresponding type declarations. +decoratorMetadataTypeOnlyUnresolvedImport.ts(3,37): error TS2307: Cannot find module './missing-default-specifier' or its corresponding type declarations. +decoratorMetadataTypeOnlyUnresolvedImport.ts(13,11): error TS1361: 'Foo' cannot be used as a value because it was imported using 'import type'. +decoratorMetadataTypeOnlyUnresolvedImport.ts(14,11): error TS1361: 'Bar' cannot be used as a value because it was imported using 'import type'. +decoratorMetadataTypeOnlyUnresolvedImport.ts(15,11): error TS1361: 'Baz' cannot be used as a value because it was imported using 'import type'. + + +==== decoratorMetadataTypeOnlyUnresolvedImport.ts (6 errors) ==== + import type Foo from "./missing-default"; + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2307: Cannot find module './missing-default' or its corresponding type declarations. + import { type Bar } from "./missing-named"; + ~~~~~~~~~~~~~~~~~ +!!! error TS2307: Cannot find module './missing-named' or its corresponding type declarations. + import type { default as Baz } from "./missing-default-specifier"; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2307: Cannot find module './missing-default-specifier' or its corresponding type declarations. + + function dec(_target: unknown, _key: unknown) {} + + class C { + @dec a!: Foo; + @dec b!: Bar; + @dec c!: Baz; + } + + const x = Foo; + ~~~ +!!! error TS1361: 'Foo' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 decoratorMetadataTypeOnlyUnresolvedImport.ts:1:8: 'Foo' was imported here. + const y = Bar; + ~~~ +!!! error TS1361: 'Bar' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 decoratorMetadataTypeOnlyUnresolvedImport.ts:2:10: 'Bar' was imported here. + const z = Baz; + ~~~ +!!! error TS1361: 'Baz' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 decoratorMetadataTypeOnlyUnresolvedImport.ts:3:15: 'Baz' was imported here. + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).js b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).js new file mode 100644 index 0000000000000..cfecbceb93e65 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).js @@ -0,0 +1,49 @@ +//// [tests/cases/compiler/decoratorMetadataTypeOnlyUnresolvedImport.ts] //// + +//// [decoratorMetadataTypeOnlyUnresolvedImport.ts] +import type Foo from "./missing-default"; +import { type Bar } from "./missing-named"; +import type { default as Baz } from "./missing-default-specifier"; + +function dec(_target: unknown, _key: unknown) {} + +class C { + @dec a!: Foo; + @dec b!: Bar; + @dec c!: Baz; +} + +const x = Foo; +const y = Bar; +const z = Baz; + + +//// [decoratorMetadataTypeOnlyUnresolvedImport.js] +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +function dec(_target, _key) { } +class C { +} +__decorate([ + dec, + __metadata("design:type", Object) +], C.prototype, "a", void 0); +__decorate([ + dec, + __metadata("design:type", Object) +], C.prototype, "b", void 0); +__decorate([ + dec, + __metadata("design:type", Object) +], C.prototype, "c", void 0); +const x = Foo; +const y = Bar; +const z = Baz; +export {}; diff --git a/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).symbols b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).symbols new file mode 100644 index 0000000000000..9281d089b8443 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).symbols @@ -0,0 +1,48 @@ +//// [tests/cases/compiler/decoratorMetadataTypeOnlyUnresolvedImport.ts] //// + +=== decoratorMetadataTypeOnlyUnresolvedImport.ts === +import type Foo from "./missing-default"; +>Foo : Symbol(Foo, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 0, 6)) + +import { type Bar } from "./missing-named"; +>Bar : Symbol(Bar, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 1, 8)) + +import type { default as Baz } from "./missing-default-specifier"; +>Baz : Symbol(Baz, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 13)) + +function dec(_target: unknown, _key: unknown) {} +>dec : Symbol(dec, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 66)) +>_target : Symbol(_target, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 4, 13)) +>_key : Symbol(_key, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 4, 30)) + +class C { +>C : Symbol(C, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 4, 48)) + + @dec a!: Foo; +>dec : Symbol(dec, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 66)) +>a : Symbol(C.a, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 6, 9)) +>Foo : Symbol(Foo, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 0, 6)) + + @dec b!: Bar; +>dec : Symbol(dec, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 66)) +>b : Symbol(C.b, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 7, 17)) +>Bar : Symbol(Bar, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 1, 8)) + + @dec c!: Baz; +>dec : Symbol(dec, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 66)) +>c : Symbol(C.c, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 8, 17)) +>Baz : Symbol(Baz, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 13)) +} + +const x = Foo; +>x : Symbol(x, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 12, 5)) +>Foo : Symbol(Foo, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 0, 6)) + +const y = Bar; +>y : Symbol(y, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 13, 5)) +>Bar : Symbol(Bar, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 1, 8)) + +const z = Baz; +>z : Symbol(z, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 14, 5)) +>Baz : Symbol(Baz, Decl(decoratorMetadataTypeOnlyUnresolvedImport.ts, 2, 13)) + diff --git a/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).types b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).types new file mode 100644 index 0000000000000..8264a6bd572ac --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/decoratorMetadataTypeOnlyUnresolvedImport(module=esnext).types @@ -0,0 +1,46 @@ +//// [tests/cases/compiler/decoratorMetadataTypeOnlyUnresolvedImport.ts] //// + +=== decoratorMetadataTypeOnlyUnresolvedImport.ts === +import type Foo from "./missing-default"; +>Foo : any + +import { type Bar } from "./missing-named"; +>Bar : any + +import type { default as Baz } from "./missing-default-specifier"; +>default : any +>Baz : any + +function dec(_target: unknown, _key: unknown) {} +>dec : (_target: unknown, _key: unknown) => void +>_target : unknown +>_key : unknown + +class C { +>C : C + + @dec a!: Foo; +>dec : (_target: unknown, _key: unknown) => void +>a : Foo + + @dec b!: Bar; +>dec : (_target: unknown, _key: unknown) => void +>b : Bar + + @dec c!: Baz; +>dec : (_target: unknown, _key: unknown) => void +>c : Baz +} + +const x = Foo; +>x : any +>Foo : any + +const y = Bar; +>y : any +>Bar : any + +const z = Baz; +>z : any +>Baz : any + diff --git a/tsc/testdata/tests/cases/compiler/decoratorMetadataTypeOnlyUnresolvedImport.ts b/tsc/testdata/tests/cases/compiler/decoratorMetadataTypeOnlyUnresolvedImport.ts new file mode 100644 index 0000000000000..52941c6662a61 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/decoratorMetadataTypeOnlyUnresolvedImport.ts @@ -0,0 +1,21 @@ +// @module: commonjs, esnext +// @target: es2015 +// @strict: true +// @experimentalDecorators: true +// @emitDecoratorMetadata: true + +import type Foo from "./missing-default"; +import { type Bar } from "./missing-named"; +import type { default as Baz } from "./missing-default-specifier"; + +function dec(_target: unknown, _key: unknown) {} + +class C { + @dec a!: Foo; + @dec b!: Bar; + @dec c!: Baz; +} + +const x = Foo; +const y = Bar; +const z = Baz; From dcd66eb1ac90dba05cbdc44883a3e9584250c277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 21 Oct 2026 15:32:17 +0200 Subject: [PATCH 2/3] Add baseline coverage for type-only import reexports --- ...lvedDefaultImportReexport(module=commonjs).js | 16 ++++++++++++++++ ...solvedDefaultImportReexport(module=esnext).js | 14 ++++++++++++++ .../typeOnlyUnresolvedDefaultImportReexport.ts | 13 +++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).js create mode 100644 tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).js create mode 100644 tsc/testdata/tests/cases/compiler/typeOnlyUnresolvedDefaultImportReexport.ts diff --git a/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).js b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).js new file mode 100644 index 0000000000000..4d7623740c735 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).js @@ -0,0 +1,16 @@ +//// [tests/cases/compiler/typeOnlyUnresolvedDefaultImportReexport.ts] //// + +//// [a.d.ts] +import type Foo from "./missing"; +export { Foo }; + +//// [main.ts] +import { Foo } from "./a"; +const x = Foo; + + +//// [main.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const a_1 = require("./a"); +const x = a_1.Foo; diff --git a/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).js b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).js new file mode 100644 index 0000000000000..57ea5a6e13cf7 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).js @@ -0,0 +1,14 @@ +//// [tests/cases/compiler/typeOnlyUnresolvedDefaultImportReexport.ts] //// + +//// [a.d.ts] +import type Foo from "./missing"; +export { Foo }; + +//// [main.ts] +import { Foo } from "./a"; +const x = Foo; + + +//// [main.js] +import { Foo } from "./a"; +const x = Foo; diff --git a/tsc/testdata/tests/cases/compiler/typeOnlyUnresolvedDefaultImportReexport.ts b/tsc/testdata/tests/cases/compiler/typeOnlyUnresolvedDefaultImportReexport.ts new file mode 100644 index 0000000000000..bdbf237f20c14 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/typeOnlyUnresolvedDefaultImportReexport.ts @@ -0,0 +1,13 @@ +// @module: commonjs, esnext +// @target: es2015 +// @strict: true +// @skipLibCheck: true +// @noTypesAndSymbols: true + +// @filename: a.d.ts +import type Foo from "./missing"; +export { Foo }; + +// @filename: main.ts +import { Foo } from "./a"; +const x = Foo; From 1181940c25823d28025a12233920265636a0e4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 21 Oct 2026 15:32:18 +0200 Subject: [PATCH 3/3] Compute type-only alias markers before propagating them --- tsc/internal/checker/checker.go | 5 +++-- ...faultImportReexport(module=commonjs).errors.txt | 14 ++++++++++++++ ...solvedDefaultImportReexport(module=commonjs).js | 3 +-- ...DefaultImportReexport(module=esnext).errors.txt | 14 ++++++++++++++ ...resolvedDefaultImportReexport(module=esnext).js | 2 +- 5 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).errors.txt diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index a85e78b47cf45..6f858215013ac 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -16700,9 +16700,10 @@ func (c *Checker) resolveAlias(symbol *ast.Symbol) *ast.Symbol { func (c *Checker) resolveIndirectionAlias(source *ast.Symbol, target *ast.Symbol) *ast.Symbol { result := c.getMergedSymbol(c.resolveAlias(target)) - if targetLinks := c.aliasSymbolLinks.Get(target); targetLinks.typeOnlyDeclaration != nil { + // Compute the target's type-only marker before propagating it, even if alias resolution failed. + if typeOnlyDeclaration := c.getTypeOnlyAliasDeclaration(target); typeOnlyDeclaration != nil { if sourceLinks := c.aliasSymbolLinks.Get(source); sourceLinks.typeOnlyDeclaration == nil { - sourceLinks.typeOnlyDeclaration = targetLinks.typeOnlyDeclaration + sourceLinks.typeOnlyDeclaration = typeOnlyDeclaration } } return result diff --git a/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).errors.txt b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).errors.txt new file mode 100644 index 0000000000000..7b599ce95877e --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).errors.txt @@ -0,0 +1,14 @@ +main.ts(2,11): error TS1361: 'Foo' cannot be used as a value because it was imported using 'import type'. + + +==== a.d.ts (0 errors) ==== + import type Foo from "./missing"; + export { Foo }; + +==== main.ts (1 errors) ==== + import { Foo } from "./a"; + const x = Foo; + ~~~ +!!! error TS1361: 'Foo' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 a.d.ts:1:8: 'Foo' was imported here. + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).js b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).js index 4d7623740c735..c256b499168c0 100644 --- a/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).js +++ b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=commonjs).js @@ -12,5 +12,4 @@ const x = Foo; //// [main.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const a_1 = require("./a"); -const x = a_1.Foo; +const x = Foo; diff --git a/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).errors.txt b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).errors.txt new file mode 100644 index 0000000000000..7b599ce95877e --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).errors.txt @@ -0,0 +1,14 @@ +main.ts(2,11): error TS1361: 'Foo' cannot be used as a value because it was imported using 'import type'. + + +==== a.d.ts (0 errors) ==== + import type Foo from "./missing"; + export { Foo }; + +==== main.ts (1 errors) ==== + import { Foo } from "./a"; + const x = Foo; + ~~~ +!!! error TS1361: 'Foo' cannot be used as a value because it was imported using 'import type'. +!!! related TS1376 a.d.ts:1:8: 'Foo' was imported here. + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).js b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).js index 57ea5a6e13cf7..ae0ff7cfaa337 100644 --- a/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).js +++ b/tsc/testdata/baselines/reference/compiler/typeOnlyUnresolvedDefaultImportReexport(module=esnext).js @@ -10,5 +10,5 @@ const x = Foo; //// [main.js] -import { Foo } from "./a"; const x = Foo; +export {};