Skip to content

Module resolution: "nodenext" fails to follow imports in .d.ts files of dependency packages that are "type": "module" #62490

@Harris-Miller

Description

@Harris-Miller

Demo Repo

https://github.com/Harris-Miller/moduleResolutionIssue

Which of the following problems are you reporting?

The module specifier resolves at runtime, but not at build time

Demonstrate the defect described above with a code sample.

https://github.com/Harris-Miller/moduleResolutionIssue/blob/main/app/src/fib.ts#L1

import { add, append } from 'test-lib';

Produces error TS2305: Module '"test-lib"' has no exported member 'add'.

add comes from a local dependency; the declaration file can be found in dist/math.d.ts
If you search for the word "math" in my attached tsc --traceResolution you'll find

Loading module as file / folder, candidate module location '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/lib/dist/math', target file types: TypeScript, JavaScript, Declaration, JSON.
Directory '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/lib/dist/math' does not exist, skipping all lookups in it.
======== Module name './math' was not resolved. ========

Import to the failed resolution is that libs/package.json is configured as "type": "module"
It appears that when using "moduleResolution": "nodenext", when resolving files of dependencies, it's applying resolution for each depent package to that package's package.json#type value. That makes sense for runtime code, but breaks for declaration .d.ts files, which can never be built with important statements containing file extensions

Run tsc --showConfig and paste its output here

{
    "compilerOptions": {
        "rootDir": "./src",
        "outDir": "./dist",
        "module": "nodenext",
        "moduleResolution": "nodenext",
        "types": [],
        "sourceMap": false,
        "declaration": false,
        "declarationMap": false,
        "noUncheckedIndexedAccess": false,
        "exactOptionalPropertyTypes": true,
        "strict": true,
        "jsx": "react-jsx",
        "verbatimModuleSyntax": false,
        "isolatedModules": true,
        "noUncheckedSideEffectImports": true,
        "moduleDetection": "force",
        "skipLibCheck": true,
        "target": "esnext",
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "resolvePackageJsonExports": true,
        "resolvePackageJsonImports": true,
        "resolveJsonModule": true,
        "preserveConstEnums": true,
        "useDefineForClassFields": true,
        "noImplicitAny": true,
        "noImplicitThis": true,
        "strictNullChecks": true,
        "strictFunctionTypes": true,
        "strictBindCallApply": true,
        "strictPropertyInitialization": true,
        "strictBuiltinIteratorReturn": true,
        "alwaysStrict": true,
        "useUnknownInCatchVariables": true
    },
    "files": [
        "./src/fib.ts",
        "./src/main.ts"
    ],
    "exclude": [
        "/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/dist"
    ]
}

Run tsc --traceResolution and paste its output here

File '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/src/package.json' does not exist.
Found 'package.json' at '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/package.json'.
======== Resolving module 'react/jsx-runtime' from '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/src/fib.ts'. ========
Explicitly specified module resolution kind: 'NodeNext'.
Resolving in CJS mode with conditions 'require', 'types', 'node'.
File '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/src/package.json' does not exist according to earlier cached lookups.
File '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/package.json' exists according to earlier cached lookups.

...

======== Resolving module 'test-lib' from '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/src/fib.ts'. ========
Explicitly specified module resolution kind: 'NodeNext'.
Resolving in CJS mode with conditions 'require', 'types', 'node'.
File '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/src/package.json' does not exist according to earlier cached lookups.
File '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/package.json' exists according to earlier cached lookups.
Loading module 'test-lib' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.
Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.
Directory '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/src/node_modules' does not exist, skipping all lookups in it.
Found 'package.json' at '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/node_modules/test-lib/package.json'.
Entering conditional exports.
Matched 'exports' condition 'types'.
Using 'exports' subpath '.' with target './dist/index.d.ts'.
File '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/node_modules/test-lib/dist/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolved under condition 'types'.
Exiting conditional exports.
Resolving real path for '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/node_modules/test-lib/dist/index.d.ts', result '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/lib/dist/index.d.ts'.
======== Module name 'test-lib' was successfully resolved to '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/lib/dist/index.d.ts' with Package ID 'test-lib/dist/index.d.ts@1.0.0'. ========
File '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/lib/dist/package.json' does not exist.
Found 'package.json' at '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/lib/package.json'.
======== Resolving module './math' from '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/lib/dist/index.d.ts'. ========
Explicitly specified module resolution kind: 'NodeNext'.
Resolving in ESM mode with conditions 'import', 'types', 'node'.
Loading module as file / folder, candidate module location '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/lib/dist/math', target file types: TypeScript, JavaScript, Declaration, JSON.
Directory '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/lib/dist/math' does not exist, skipping all lookups in it.
======== Module name './math' was not resolved. ========
File '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/src/package.json' does not exist according to earlier cached lookups.
File '/Users/hmiller/Projects/harris-miller/moduleResolutionIssue/app/package.json' exists according to earlier cached lookups.

...

src/fib.ts(1,10): error TS2305: Module '"test-lib"' has no exported member 'add'.

Paste the package.json of the importing module, if it exists

{
  "name": "test-app",
  "private": true,
  "version": "1.0.0",
  "description": "",
  "main": "dist/main.js",
  "scripts": {
    "build": "tsc",
    "start": "node ./dist/main.js",
    "typecheck": "tsc --noEmit"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "test-lib": "file://../lib"
  },
  "devDependencies": {
    "@types/node": "^22.18.6",
    "tslib": "^2.8.1",
    "typescript": "^5.9.2"
  }
}

Paste the package.json of the target module, if it exists

{
  "name": "test-lib",
  "type": "module",
  "version": "1.0.0",
  "description": "",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    }
  },
  "files": [
    "dist"
  ],
  "scripts": {
    "build": "rollup -c",
    "typecheck": "tsc --noEmit"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@rollup/plugin-typescript": "^12.1.4",
    "@types/node": "^22.18.6",
    "rollup": "^4.52.2",
    "tslib": "^2.8.1",
    "typescript": "^5.9.2"
  }
}

Any other comments can go here

I removed the unimportant bits in my pasted tsc --traceResolition for brevity

Typescript doesn't have a problem resolving "types": "./dist/index.d.ts" since there is an extension there, the problem is for then that file imports others without extensions, which is always

Metadata

Metadata

Assignees

No one assigned

    Labels

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions