How to emit TS declarations for legacy CommonJS dependencies with correct "module"

1.1k Views Asked by At

I'm trying to generate type declarations for old dependencies I use, which emit CJS modules and have no typings of their own. For example, the aabb-3d module (though this issue isn't specific to that module).

I'm generating the declaration with a command like:

tsc node_modules/aabb-3d/index.js 
    --allowJs 
    --declaration 
    --emitDeclarationOnly 
    --outFile types/aabb-3d/index.d.ts

This works, and the declaration gets generated, but the contents of the file look like:

declare module "index" {
    export = AABB;
    function AABB(pos: any, vec: any): AABB;
    //...

When I then try to edit code, my editor doesn't pick up the typings because it expects the declaration to be declaring the module aabb-3d, not index.

If I manually change the generated d.ts file, my editor works and code hints work correctly for the legacy module. How can I get tsc to generate working declaration files?

1

There are 1 best solutions below

3
On BEST ANSWER

I checked TypeScript's own code, and there's no evident way to change how the declare module line is generated. Fortunately, you can just prevent it from being generated at all. You're getting an ambient external module declaration (declare module) because you're using --outFile instead of --outDir. Here's how to generate the same types/aabb-3d/index.ts file as an ES-style module declaration without declare module:

tsc node_modules/aabb-3d/index.js 
    --allowJs 
    --declaration 
    --emitDeclarationOnly 
    --outDir types/aabb-3d

In order for TypeScript to find the ES-style module declarations in the types tree, you'll need the following in your project's tsconfig.json:

{
    ...
    "compilerOptions": {
        ...
        "typeRoots": [
            "./types",
            "./node_modules/@types"
        ],
        "baseUrl": "./",
        "paths": {
            "*": [
                "./types/*",
                "*"
            ]
        }
        ...
    }
    ...
}

Then you can import aabb3d from 'aabb-3d' in your code with the typing in place, such as it is when tsc auto-generates it.

In case a package's "main" file is not named index.js and thus the generated declarations are not named index.d.ts, you will need to put a package.json file in the types/$package directory to point the compiler to the right file. For example, if the main file were main.js, making the declarations file main.d.ts, the package.json would be:

{
    "types": "main.d.ts"
}