Summary
consola@3.4.2's legacy declaration entry, dist/index.d.ts, combines an export assignment with
named exports:
declare const consola: ConsolaInstance;
// @ts-ignore
export = consola;
export { ConsolaInstance, ConsolaOptions, consola, createConsola };
That is invalid TypeScript — export = cannot coexist with other exported elements (TS2309) — and the
// @ts-ignore silences the error inside consola's own file. Under tsc this works out: the error is
suppressed and the named exports remain usable, so import { consola } from "consola" type-checks.
Under TypeScript 7 / tsgo it does not. tsgo raises an additional diagnostic at the
consumer's import site:
error TS2595: 'consola' can only be imported by using a default import.
A consumer cannot suppress that — the @ts-ignore lives in consola, and the error is reported in the
importing file. So a correct, documented import becomes an unfixable error for downstream projects.
Why it bites even though exports is correct
The exports map is fine: exports["."].node.import.types → dist/index.d.mts, which does a clean
export { …, consola, createConsola, consola as default }. The problem is only the legacy top-level
types field → dist/index.d.ts.
Any consumer whose resolution reaches that legacy entry rather than the exports map gets the invalid
file. That is not exotic: Nuxt generates compilerOptions.paths entries mapping bare package names to
package directories (to pin exact versions in a pnpm workspace), e.g.
"paths": { "consola": ["../../../node_modules/.pnpm/consola@3.4.2/node_modules/consola"] }
Directory resolution consults types/main, not exports — in tsc and tsgo alike (verified with
--traceResolution on both). So in a Nuxt project every import { consola } from "consola" resolves
through the invalid declaration file. We hit this on 8 import sites in a Nuxt 4 monorepo.
Confirmed with a faux package carrying the same shape (invalid legacy index.d.ts + valid
index.d.mts behind an exports map), toggling only the paths entry:
| tsconfig |
tsgo |
tsc |
with paths: { faux: ["./node_modules/faux"] } |
TS2595 at the consumer |
clean |
without paths (so exports is consulted) |
clean |
clean |
That isolates it precisely: the exports map is fine and resolving through it is clean on both
compilers. Only the legacy types entry is broken, and it is reachable in real projects.
Suggested fix
Make dist/index.d.ts valid, so the legacy entry is safe for consumers that reach it. Either:
- Drop
export = consola from dist/index.d.ts and keep only the named + default exports (matching
index.d.mts); or
- If
export = must stay for CJS require() consumers, keep it in dist/index.d.cts only — which
already uses it correctly and on its own — and point the legacy types field at a valid file.
Either removes the need for the // @ts-ignore and makes consola forward-compatible with TypeScript 7
without downstream changes.
Environment
- consola 3.4.2
tsc 5.9.3 — accepts the named import (no diagnostics)
tsgo @typescript/native-preview@7.0.0-dev.20260707.2 — TS2595 at each consumer import site
- Also reproduces via
vue-tsc on typescript-native-bridge (tsgo 7.0.2 behind the classic API)
moduleResolution: "Bundler", module: "preserve", strict: true
Minimal repro (no consola needed): a 5-line .d.ts with the same export = + named-exports +
@ts-ignore shape, imported by one line, reproduces TS2595 on tsgo and is clean on tsc. Filed
upstream at microsoft/typescript-go as well, since tsgo arguably should not surface a consumer-side
error for a suppressed declaration-side one — but fixing the declaration file resolves it regardless of
what upstream decides.
Summary
consola@3.4.2's legacy declaration entry,dist/index.d.ts, combines an export assignment withnamed exports:
That is invalid TypeScript —
export =cannot coexist with other exported elements (TS2309) — and the// @ts-ignoresilences the error inside consola's own file. Undertscthis works out: the error issuppressed and the named exports remain usable, so
import { consola } from "consola"type-checks.Under TypeScript 7 /
tsgoit does not. tsgo raises an additional diagnostic at theconsumer's import site:
A consumer cannot suppress that — the
@ts-ignorelives in consola, and the error is reported in theimporting file. So a correct, documented import becomes an unfixable error for downstream projects.
Why it bites even though
exportsis correctThe
exportsmap is fine:exports["."].node.import.types→dist/index.d.mts, which does a cleanexport { …, consola, createConsola, consola as default }. The problem is only the legacy top-leveltypesfield →dist/index.d.ts.Any consumer whose resolution reaches that legacy entry rather than the
exportsmap gets the invalidfile. That is not exotic: Nuxt generates
compilerOptions.pathsentries mapping bare package names topackage directories (to pin exact versions in a pnpm workspace), e.g.
Directory resolution consults
types/main, notexports— intscandtsgoalike (verified with--traceResolutionon both). So in a Nuxt project everyimport { consola } from "consola"resolvesthrough the invalid declaration file. We hit this on 8 import sites in a Nuxt 4 monorepo.
Confirmed with a faux package carrying the same shape (invalid legacy
index.d.ts+ validindex.d.mtsbehind anexportsmap), toggling only thepathsentry:tsgotscpaths: { faux: ["./node_modules/faux"] }TS2595at the consumerpaths(soexportsis consulted)That isolates it precisely: the
exportsmap is fine and resolving through it is clean on bothcompilers. Only the legacy
typesentry is broken, and it is reachable in real projects.Suggested fix
Make
dist/index.d.tsvalid, so the legacy entry is safe for consumers that reach it. Either:export = consolafromdist/index.d.tsand keep only the named + default exports (matchingindex.d.mts); orexport =must stay for CJSrequire()consumers, keep it indist/index.d.ctsonly — whichalready uses it correctly and on its own — and point the legacy
typesfield at a valid file.Either removes the need for the
// @ts-ignoreand makes consola forward-compatible with TypeScript 7without downstream changes.
Environment
tsc5.9.3 — accepts the named import (no diagnostics)tsgo@typescript/native-preview@7.0.0-dev.20260707.2—TS2595at each consumer import sitevue-tscontypescript-native-bridge(tsgo 7.0.2 behind the classic API)moduleResolution: "Bundler",module: "preserve",strict: trueMinimal repro (no consola needed): a 5-line
.d.tswith the sameexport =+ named-exports +@ts-ignoreshape, imported by one line, reproducesTS2595on tsgo and is clean on tsc. Filedupstream at microsoft/typescript-go as well, since tsgo arguably should not surface a consumer-side
error for a suppressed declaration-side one — but fixing the declaration file resolves it regardless of
what upstream decides.