Skip to content

Incorrect declaration output when using outFile and referencing a local module that shares the package name of an external or built-in module #37674

@wessberg

Description

@wessberg

TypeScript Version: 3.8.3

Search Terms: outFile, declaration, bare module specifiers, conflict

Problem:

When using the outFile feature of TypeScript in combination with generation of declarations, all local modules are inlined with bare module specifiers inside the generated .d.ts file. For example, the source folder structure:

- src/
   - a.ts
   - b.ts

...Would lead to declaration output following this structure:

declare module "a" {
    // ...
}
declare module "b" {
    // ...
}

This is fine and won't generally lead to any problems.

However, when filenames of project-local modules overlap with package names of external or built-in modules, and both are referenced from within the same code base, this may lead to issues under some circumstances, as can be seen in the provided repro by running tsc, since both the module specifier for the project-local module, as well as the external one will be identical.

Code

For example, in a project where @types/node is installed, with the folder structure:

- index.ts
- fs.ts

And the code:

// Inside index.ts

import {readFileSync} from "fs"; // (1)
import {readFileSync as localReadFileSync} from "./fs"; // (2)
export const foo = readFileSync;
export const bar = localReadFileSync;

// Inside fs.ts

export function readFileSync () {}

The ImportDeclaration labeled (1) references the built-in fs module via the node module resolution algorithm, whereas the ImportDeclaration (2) references the project-local module of the same name, as a relative path.

However, given that the outFile feature converts everything to bare module specifiers, here's what is produced by tsc:

/// <reference types="node" />
declare module "fs" {
    export function readFileSync(): void;
}
declare module "index" {
    import { readFileSync } from "fs";
    import { readFileSync as localReadFileSync } from "fs";
    export const foo: typeof readFileSync;
    export const bar: typeof localReadFileSync;
}

This does not reflect the source contents, given that the initial module declaration (declare module "fs") will take precedence over the external, built-in fs module. Note that this does not only relate to built-in modules, but is just as relevant for installable packages.

Expected behavior:

The local module of the same name, in this example fs.ts, would have a unique module specifier inside the generated declarations such that references to the local file and the external module of the same name won't conflict in the declaration output.

Actual behavior:

The local module of the same name, in this example fs.ts will take precedence over the external one, given that a ModuleDeclaration (declare module "fs") will be added to the declaration output, leading to incorrect declarations.

Playground Link:

I've made a repro you can take a look at: https://github.com/wessberg/typescript-out-file-bug

  1. Run npm install
  2. Run npm run build or simply tsc to generate the declaration output containing the bug.
  3. Check it out in dist/dist.d.ts

Related Issues:
None inside of this repository, but it is partly related to an issue that breaks declaration bundling inside rollup-plugin-ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    UnactionableThere isn't something we can do with this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions