Skip to content

maxNodeModuleJsDepth + allowJs does not load types from a CommonJS dependency in node_modules (TS7016 vs tsc 6.0 success) #3493

Description

@tomquist

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.)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions