When a project uses allowImportingTsExtensions-style relative imports (import { x } from './helper.ts'), tsgo's declaration emit prints synthesized import() type references to package subpaths without their file extension. Under --module nodenext the extensionless specifier does not resolve, so the emitted .d.ts is invalid. Emit succeeds with no diagnostic.
Reproduced with typescript@7.0.2 and nightly @typescript/native-preview@7.0.0-dev.20260707.2.
Steps to reproduce
Self-contained — no npm install; the dependency is materialized by the script (a plain CJS package with no exports map, imported via an explicit-extension subpath, like fp-ts among others):
mkdir repro && cd repro && mkdir -p src node_modules/mylib/lib
cat > package.json <<'EOF'
{ "name": "repro", "type": "module" }
EOF
cat > tsconfig.json <<'EOF'
{
"compilerOptions": {
"module": "nodenext",
"declaration": true,
"emitDeclarationOnly": true,
"allowImportingTsExtensions": true,
"rootDir": "src",
"outDir": "out"
},
"include": ["src"]
}
EOF
cat > node_modules/mylib/package.json <<'EOF'
{ "name": "mylib", "version": "1.0.0", "main": "./lib/index.js" }
EOF
cat > node_modules/mylib/lib/Box.js <<'EOF'
exports.box = (value) => ({ value });
EOF
cat > node_modules/mylib/lib/Box.d.ts <<'EOF'
export interface Box<T> { readonly value: T }
export declare function box<T>(value: T): Box<T>;
EOF
cat > src/helper.ts <<'EOF'
import * as B from 'mylib/lib/Box.js';
export const inner = B.box('hello');
EOF
cat > src/mod.ts <<'EOF'
import { inner } from './helper.ts';
export const out = inner;
EOF
Then compile with each compiler and inspect out/mod.d.ts, as shown in the two sections below.
Behavior with typescript@6.0
npx -y -p typescript@6.0.2 tsc -p tsconfig.json
cat out/mod.d.ts
Emits a resolvable specifier:
export declare const out: import("mylib/lib/Box.js").Box<string>;
Behavior with tsgo
npx -y -p typescript@7.0.2 tsc -p tsconfig.json
cat out/mod.d.ts
The extension is dropped:
export declare const out: import("mylib/lib/Box").Box<string>;
The trigger is the .ts-extension relative import
One-character bisect: change src/mod.ts to import './helper.js' instead of './helper.ts' and tsgo emits the extension correctly. Generics, type aliases, and rewriteRelativeImportExtensions were all eliminated as factors.
The emitted file is invalid
cat > tsconfig.verify.json <<'EOF'
{
"compilerOptions": { "module": "nodenext", "noEmit": true, "skipLibCheck": false },
"include": ["out/mod.d.ts"]
}
EOF
npx -y -p typescript@7.0.2 tsc -p tsconfig.verify.json
out/mod.d.ts(1,34): error TS2307: Cannot find module 'mylib/lib/Box' or its corresponding type declarations.
(typescript@6.0.2 rejects it identically)
In a monorepo with skipLibCheck: true this is silent — the broken reference degrades to an error type and surfaces as unrelated-looking inference failures ('x' is of type 'unknown') in downstream packages.
Disclosure: this bug was found, minimized, and verified interactively by a human (me) working with Claude Code; the write-up was drafted by the model and reviewed by me. All commands and outputs above were executed and confirmed against the stated versions.
When a project uses
allowImportingTsExtensions-style relative imports (import { x } from './helper.ts'), tsgo's declaration emit prints synthesizedimport()type references to package subpaths without their file extension. Under--module nodenextthe extensionless specifier does not resolve, so the emitted.d.tsis invalid. Emit succeeds with no diagnostic.Reproduced with
typescript@7.0.2and nightly@typescript/native-preview@7.0.0-dev.20260707.2.Steps to reproduce
Self-contained — no
npm install; the dependency is materialized by the script (a plain CJS package with noexportsmap, imported via an explicit-extension subpath, like fp-ts among others):Then compile with each compiler and inspect
out/mod.d.ts, as shown in the two sections below.Behavior with
typescript@6.0Emits a resolvable specifier:
Behavior with
tsgoThe extension is dropped:
The trigger is the
.ts-extension relative importOne-character bisect: change
src/mod.tsto import'./helper.js'instead of'./helper.ts'and tsgo emits the extension correctly. Generics, type aliases, andrewriteRelativeImportExtensionswere all eliminated as factors.The emitted file is invalid
(
typescript@6.0.2rejects it identically)In a monorepo with
skipLibCheck: truethis is silent — the broken reference degrades to an error type and surfaces as unrelated-looking inference failures ('x' is of type 'unknown') in downstream packages.Disclosure: this bug was found, minimized, and verified interactively by a human (me) working with Claude Code; the write-up was drafted by the model and reviewed by me. All commands and outputs above were executed and confirmed against the stated versions.