Generating TypeScript declarations for re-exported JS functions in a Node.js module

1.1k Views Asked by At

I am trying to emit declarations for types and functions annotated with JSDoc. Those are useful for TypeScript users and generating them from JSDoc means less overhead on our SDK developers.

TypeScript users should get one module called Apify and from that access our SDK functions.

import {Apify} from "apify";

const envs = Apify.getEnv();
...

This does not happen and the generated index.d.ts contains multiple modules, one for each JS source file.


The situation

My JavaScript library is a single module visible to the client code. Source code is composed of multiple files in src/ directory:

  • src/index.js
  • src/actor.js
  • src/request.js
  • ...

The index.js file re-exports functions defined in other files, so they can be accessed by client code running in Node.js.

import { main, getEnv, call, callTask, ... } from './actor';
import Request from './request';
...

/**
 * The following section describes all functions and properties provided by the `apify` package...
 *
 * @module Apify
 */
module.exports = {
    main,
    getEnv,
    ...
    Request,
    ...
};

In package.json, the file build/index.js is defined as the entry-point (after being transpiled by babel from src/index.js):

{
   "main": "build/index.js",
   ...
}

My tsconfig.json is the following:

{
    "compilerOptions": {
        "target": "esnext",
        "module": "esnext",
        "moduleResolution": "node",
        "lib": [
            "es2017",
            "dom"
        ],
        "allowJs": true,
        "checkJs": false,
        "noEmit": false,
        "declaration": true,
        "emitDeclarationOnly": true,
        "strict": false,
        "noImplicitThis": true,
        "alwaysStrict": true,
        "esModuleInterop": true,
        "outFile": "types/index.d.ts"
    },
    "include": [
        "src/index.js"
    ]
}


Result

After running tsc I get my types/index.d.ts, with module declarations for each of my JS source file (each of them is inaccessible by the client code) and an empty index module:

declare module "actor" {
    export function getEnv(): Env;
    export function main(userFunc: Function): void;
    ...
}
declare module "request" { ... }
...

declare module "index" {
    export {};
}
2

There are 2 best solutions below

0
On

You can add this on the index:

export {main, getEnv} from './actor';
1
On

this issue is because you are using module.exports instead of export default

change

/**
 * The following section describes all functions and properties provided by the `apify` package...
 *
 * @module Apify
 */
module.exports = {
    main,
    getEnv,
    ...
    Request,
    ...
};

to


/**
 * The following section describes all functions and properties provided by the `apify` package...
 *
 * @module Apify
 */
export default {
    main,
    getEnv,
    ...
    Request,
    ...
};

then for example lets look at this files

actors.js

module.exports = {
    main: () => { console.log("main") },
    getEnv: () => { console.log("getEnv") }
}

request.js

module.exports = {
    Request: () => { console.log("req") }
}

and index.js

import { main, getEnv } from './actor';
import Request from './request';

/**
 * The following section describes all functions and properties provided by the `apify` package...
 *
 * @module Apify
 */
export default {
    main,
    getEnv,
    Request,
};

types file with module.export

declare module "actor" {
    export function main(): void;
    export function getEnv(): void;
}
declare module "request" {
    export function Request(): void;
}
declare module "index" {
    export {};
}

and with export default

declare module "actor" {
    export function main(): void;
    export function getEnv(): void;
}
declare module "request" {
    export function Request(): void;
}
declare module "index" {
    namespace _default {
        export { main };
        export { getEnv };
        export { Request };
    }
    export default _default;
    import { main } from "actor";
    import { getEnv } from "actor";
    import Request from "request";
}