Skip to content

ESM type declarations use extensionless relative imports — may resolve to any under moduleResolution nodenext #28

@vanwalj

Description

@vanwalj

Summary

When the package is consumed by a TypeScript project configured with moduleResolution: "node16" or "nodenext", the bundled type declarations appear not to resolve, which could cause the public API surface to be inferred as any instead of its real types.

Observed behaviour

With skipLibCheck: false, tsc reports TS2834 against the shipped declarations, e.g. on dist/types/index.d.ts:

dist/types/index.d.ts(3,26): error TS2834: Relative import paths need explicit
file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or
'nodenext'. Consider adding an extension to the import path.

The same error seems to occur across the other .d.ts files, as they all use extensionless relative imports, e.g.:

export { bdclient } from './client';
export { BaseResult } from './models/result';

With the more common skipLibCheck: true, those errors are suppressed. In that case tsc would stay green, but the imported symbols appear to fall back to any. A minimal check:

import { bdclient } from '@brightdata/sdk';
const n: number = new bdclient({ apiKey: 'x' });

Under moduleResolution: "bundler", this line errors as expected (bdclient is not assignable to number) — i.e. the real types resolve. Under moduleResolution: "nodenext", the same line appears to be accepted silently, which would indicate the value is typed any. Type-aware lint rules (e.g. @typescript-eslint/no-unsafe-*) would then flag any use of the SDK.

Minimal reproduction

tsconfig.json:

{ "compilerOptions": { "module": "nodenext", "moduleResolution": "nodenext", "skipLibCheck": false, "noEmit": true } }

index.ts:

import { bdclient } from '@brightdata/sdk';
new bdclient({ apiKey: 'x' });

npx tsc would report TS2834 on the package declarations. Switching to "moduleResolution": "bundler" makes the errors disappear and the types resolve.

Hypothesis

The package is published as "type": "module" with exports.types pointing at ESM .d.ts files, but those declarations use extensionless relative imports. node16/nodenext resolution requires explicit file extensions for ESM-relative imports, so TypeScript would be unable to resolve the internal modules, and the top-level re-exports would degrade. It looks like the declarations might have been emitted / validated under moduleResolution: "bundler" (where extensionless imports are allowed), which could explain why they resolve there but not under node16/nodenext. This would be independent of the TypeScript major version — the requirement has existed since node16 resolution was introduced — but is more likely to be encountered as projects adopt nodenext.

Possible direction

Emitting explicit .js extensions in the declaration output (for example via rewriteRelativeImportExtensions, or by authoring the relative imports with .js extensions) so the shipped .d.ts are node16/nodenext-compatible would, in principle, let the types resolve under those resolution modes.

Environment

  • @brightdata/sdk 1.1.0
  • Appears with moduleResolution: node16 / nodenext
  • Resolves correctly with moduleResolution: bundler

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions