logologo

Types for sub modules

Mar 1, 2021 · 5min

When you build multiple entries in a single package, you exports them with exports syntax. Like

{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    },
    "./foo": {
      "types": "./dist/foo.d.ts",
      "import": "./dist/foo.mjs",
      "require": "./dist/foo.cjs"
    },
  }
}
{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    },
    "./foo": {
      "types": "./dist/foo.d.ts",
      "import": "./dist/foo.mjs",
      "require": "./dist/foo.cjs"
    },
  }
}

Then tho you provide types field for the sub modules, most of the users still got the error:

Cannot find module 'my-pkg/foo' or its corresponding type declarations.
Cannot find module 'my-pkg/foo' or its corresponding type declarations.

Well that’s because the types field in exports will only be resolved when you add "moduleResolution": "NodeNext" to the tsconfig.json file. Which might cause more issue since not all the packages are up to date.

So when you trying to import my-pkg/foo, TypeScript actually looking for the foo.d.ts file under your package root instead of your dist folder. One solution I been used for a long time is to create a redirection file that published to npm, like:

// foo.d.ts
export { default } from './dist/foo.d.ts'
export * from './dist/foo.d.ts'
// foo.d.ts
export { default } from './dist/foo.d.ts'
export * from './dist/foo.d.ts'

Which solve the problem, but also making your root directory quite messy.

Until @tmkx shared me this solution:

{
  "typesVersions": {
    "*": {
      "*": [
        "./dist/index.d.ts",
        "./dist/*"
      ]
    }
  }
}
{
  "typesVersions": {
    "*": {
      "*": [
        "./dist/index.d.ts",
        "./dist/*"
      ]
    }
  }
}

Good day! Reference

CC BY-NC-SA 4.0 2021-PRESENT © Anthony Fu