`dist/index.d.ts` mixes `export =` with named exports (TS2309, `@ts-ignore`-d) — breaks consumers under TypeScript 7 / tsgo
Summary
[email protected]'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/[email protected]/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 = consolafromdist/index.d.tsand keep only the named + default exports (matchingindex.d.mts); or - If
export =must stay for CJSrequire()consumers, keep it indist/index.d.ctsonly — which already uses it correctly and on its own — and point the legacytypesfield 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
tsc5.9.3 — accepts the named import (no diagnostics)tsgo@typescript/[email protected]—TS2595at each consumer import site- Also reproduces via
vue-tscontypescript-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.
Source: unjs/consola