Steps to reproduce
mkdir maxjsdepth-repro && cd maxjsdepth-repro
npm init -y
npm i -D @typescript/native-preview@beta typescript@^6 pg-error-constants
tsconfig.json:
{
"compilerOptions": {
"strict": true,
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"noEmit": true,
"allowJs": true,
"maxNodeModuleJsDepth": 2,
"esModuleInterop": true,
"types": []
}
}
index.ts:
import { UNIQUE_VIOLATION } from "pg-error-constants";
console.log(UNIQUE_VIOLATION);
pg-error-constants@1.x is a plain-JS package (CommonJS, exports.UNIQUE_VIOLATION = "23505") with no .d.ts and no @types/pg-error-constants.
Tested with @typescript/native-preview@beta (Version 7.0.0-dev.20260421.2) and typescript@6.0.3.
The same divergence reproduces for several other plain-JS dependencies in real-world code: punycode.js, @sendsafely/sendsafely, bpmn-moddle, bpmn-auto-layout.
Behavior with typescript@6.0
Compiles cleanly (exit 0, no diagnostics). With allowJs: true and maxNodeModuleJsDepth: 2, tsc loads the JS file from node_modules/pg-error-constants/index.js, infers types for each exports.X = "..." assignment, and treats UNIQUE_VIOLATION as a string-typed named export.
Behavior with tsgo
index.ts(1,34): error TS7016: Could not find a declaration file for module 'pg-error-constants'. '/private/tmp/tsgo-repros/maxjsdepth/node_modules/pg-error-constants/index.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/pg-error-constants` if it exists or add a new declaration (.d.ts) file containing `declare module 'pg-error-constants';`
tsgo resolves the package's main entry to index.js (visible via --traceResolution) but then refuses to use the JS file as a source of types and reports TS7016.
maxNodeModuleJsDepth was wired up in #1189, so the option is recognized; it just doesn't seem to actually make tsgo use the JS for type inference the way tsc does. The same is true with --maxNodeModuleJsDepth 5 or other values.
This means moving any tsc-allowJs codebase to tsgo will fail on every plain-JS dependency that doesn't have a .d.ts or @types/* package — even though tsc accepts those imports cleanly.
Cross-tree variant of the same bug
The same bug bites in a multi-package layout. Take a backend that consumes a shared package via link:../shared, where shared/dist/index.d.ts re-imports a JS-only dep (e.g. validator — which does have @types/validator, but in a different node_modules tree) like so:
// shared/dist/index.d.ts
import validator from "validator";
export declare function isEmail(value: string): boolean;
After yarn workspaces focus --production strips shared/node_modules/@types/validator, only shared/node_modules/validator/index.js (plain JS) is reachable from shared/dist/index.d.ts's resolver root. The backend still has backend/node_modules/@types/validator, but tsgo doesn't fall back to it.
tsc 6.0 succeeds in this scenario when allowJs: true and maxNodeModuleJsDepth: 2 are set on the consuming project, because it parses validator/index.js and uses the inferred types.
tsgo reports:
shared/dist/index.d.ts(1,23): error TS7016: Could not find a declaration file for module 'validator'. '/.../shared/node_modules/validator/index.js' implicitly has an 'any' type.
There are types at '/.../backend/node_modules/@types/validator/index.d.ts', but this result could not be resolved when respecting package.json "exports". The '@types/validator' library may need to update its package.json or typings.
(Note: @types/validator does not have an "exports" field — the "respecting package.json exports" message appears to be tsgo's generic fallback hint.)
Steps to reproduce
tsconfig.json:{ "compilerOptions": { "strict": true, "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "noEmit": true, "allowJs": true, "maxNodeModuleJsDepth": 2, "esModuleInterop": true, "types": [] } }index.ts:pg-error-constants@1.xis a plain-JS package (CommonJS,exports.UNIQUE_VIOLATION = "23505") with no.d.tsand no@types/pg-error-constants.Tested with
@typescript/native-preview@beta(Version 7.0.0-dev.20260421.2) andtypescript@6.0.3.The same divergence reproduces for several other plain-JS dependencies in real-world code:
punycode.js,@sendsafely/sendsafely,bpmn-moddle,bpmn-auto-layout.Behavior with
typescript@6.0Compiles cleanly (exit 0, no diagnostics). With
allowJs: trueandmaxNodeModuleJsDepth: 2, tsc loads the JS file fromnode_modules/pg-error-constants/index.js, infers types for eachexports.X = "..."assignment, and treatsUNIQUE_VIOLATIONas a string-typed named export.Behavior with
tsgotsgo resolves the package's
mainentry toindex.js(visible via--traceResolution) but then refuses to use the JS file as a source of types and reports TS7016.maxNodeModuleJsDepthwas wired up in #1189, so the option is recognized; it just doesn't seem to actually make tsgo use the JS for type inference the way tsc does. The same is true with--maxNodeModuleJsDepth 5or other values.This means moving any tsc-allowJs codebase to tsgo will fail on every plain-JS dependency that doesn't have a
.d.tsor@types/*package — even though tsc accepts those imports cleanly.Cross-tree variant of the same bug
The same bug bites in a multi-package layout. Take a backend that consumes a
sharedpackage vialink:../shared, whereshared/dist/index.d.tsre-imports a JS-only dep (e.g.validator— which does have@types/validator, but in a differentnode_modulestree) like so:After
yarn workspaces focus --productionstripsshared/node_modules/@types/validator, onlyshared/node_modules/validator/index.js(plain JS) is reachable fromshared/dist/index.d.ts's resolver root. The backend still hasbackend/node_modules/@types/validator, but tsgo doesn't fall back to it.tsc 6.0 succeeds in this scenario when
allowJs: trueandmaxNodeModuleJsDepth: 2are set on the consuming project, because it parsesvalidator/index.jsand uses the inferred types.tsgo reports:
(Note:
@types/validatordoes not have an"exports"field — the "respecting package.jsonexports" message appears to be tsgo's generic fallback hint.)