Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 {};
}
like image 487
Matej 'Yin' Gagyi Avatar asked Jan 02 '20 13:01

Matej 'Yin' Gagyi


People also ask

How do you export functions in TypeScript?

Use named exports to export a function in TypeScript, e.g. export function sum() {} . The exported function can be imported by using a named import as import {sum} from './another-file' . You can use as many named exports as necessary in a single file.

How do I re export from TypeScript?

To re-export values from another file in TypeScript, make sure to export the name exports as export {myFunction, myConstant} from './another-file and the default export as export {default} from './another-file' . The values can be imported from the file that re-exported them.

What is declare module in TypeScript?

In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).


1 Answers

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";
}
like image 106
Naor Tedgi Avatar answered Oct 09 '22 00:10

Naor Tedgi