5

I have a pretty large npm module written in TypeScript, which exposes a number of types, including several interfaces. Now these interfaces do not all make sense at the root level, which is why I would like to expose them in a way that they can be imported in a somewhat nested way.

Basically, I'm looking for a way to expose interface so that the user of the module can do:

import { Foo } from 'my-module';
import { Bar } from 'my-module/sub-part';

Is this possible in TypeScript, and if so, how? What does not work is this code in the root index.ts file:

export {
  Foo,
  { Bar }
};

Please note that the .ts files are not located at the root directory of the module, and so the .js and .d.ts files aren't either.

How can I solve this?

PS: It's not about default vs named export here, since I want to have a solution for exporting interfaces in multiple nested levels.

3
  • 1
    The usual approach, AFAICT, is to put a package.json file in the sub-part directory in your package's root and use the main and types entries in that file (they're gonna be the only ones you need to specify) to refer to the .d.ts and .js files - which can be anywhere you like. Commented Oct 28, 2019 at 9:07
  • Maybe you could use: declare module "my-module/sub-part" { export interface Bar { … } }? Commented Oct 28, 2019 at 11:02
  • Edited my answer. Let me know, if that helps. Commented Oct 28, 2019 at 16:46

1 Answer 1

1

In the easiest case, you just have separate index.d.ts files inside the my-module and my-module/sub-part folder of the published npm package, so we can import my-module and my-module/sub-part separately. Example project directory:

my-module
|   dist
│   index.ts
│   package.json // types prop e.g. could point to dist/index.d.ts
│
└───sub-part
        index.ts // compiled to dist/sub-part/index.d.ts

TS makes use of its module resolution process to resolve my-module or my-module/sub-part imports (given no global type declaration like @types exists). For example, my-module in import { Foo } from 'my-module' is resolved like this:

// first, try to find file directly
node_modules/my-module.ts
node_modules/my-module.tsx
node_modules/my-module.d.ts

// look in package.json for types property pointing to a file
node_modules/my-module/package.json // <-- lands here, if "types" prop exists

// look in @types
node_modules/@types/my-module.d.ts

// treat my-module as folder and look for an index file
node_modules/my-module/index.ts
node_modules/my-module/index.tsx
node_modules/my-module/index.d.ts // <-- lands here otherwise

For the sake of completness here, my-module and my-module/sub-part could also be defined as separate global module declarations in one containing file, like:

declare module "my-module" {
  const Foo: string
}

declare module "my-module/sub-part" {
  const Bar: number
}

Finally, the client code looks like you already pictured:

import { Foo } from "my-module";
import { Bar } from "my-module/sub-part";

console.log(Foo)

Hope, it helps.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.