microsoft / microsoft/TypeScript

Type literals in declaration files are emitted with comment blocks from the wrong source file

Open
#58,143 0 comments 7 reactions 1 assignee View on GitHub

@rbuckton is already working on this.

Since Apr 10, 2024.

Domain: Comment Emit Needs Investigation
Dominant language
Go
Stars
111k
Forks
14.4k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

🔎 Search Terms

"comments", "comment emit", "declaration comments", "inconsistent comments", ".d.ts comment", ".d.ts jsdoc", "wrong file", "crlf"

🕗 Version & Regression Information
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about comments
⏯ Playground Link

https://www.typescriptlang.org/dev/bug-workbench/?noLib=true&esModuleInterop=false&target=8&sourceMap=true&declarationMap=true&inlineSources=true&generateTrace=true&ts=5.4.4#code/PTAEAEBMFMGMBsCGAnRAXAlgewHYC5Q1kBXaAKBAgGcALLAdwFEBbDNCscWhlttaSADEM8aAQBGKAHSQpaKhwgAzEdByJmY0EqxY5CygHlBggMqMAKlOs3bd+w6lloADwAOWZGlCxcVb1jiAFagALygAN5koKDAAFRxoADCWMyaON46yKAABm7IWG45oHHA0aD5hQQARNUANGQAvgDcZIrgKqLqmhLS8mQYzB5ekaCBIY3aBcyg1VLAOljVrc7unt6+OP6gLmGR5QCCBOMN5fGJKWlqmZ65AELFpeV3x8ENMeVJr0ENLWRAA

💻 Code
// @declaration: true
// @showEmit
// @showEmittedFile: bar.d.ts
// @filename: foo.ts
// OFFSET.............................
export const obj = {
  /** Comment for `prop` */
  prop: "",
};

// @filename: bar.ts
import { obj } from "./foo";

export const x = {
  A: obj,

  /** Comment for `B` */
  B: obj,
  
  C: obj,
};

Workbench Repro

🙁 Actual behavior

The emitted bar.d.ts declaration file includes the comment on line 17 (preceding the B property assignment in bar.ts) above each prop signature:

export declare const x: {
    A: {
        /** Comment for `B` */
        prop: string;
    };
    /** Comment for `B` */
    B: {
        /** Comment for `B` */
        prop: string;
    };
    C: {
        /** Comment for `B` */
        prop: string;
    };
};

Note that the OFFSET... comment on line 5 (in its entirety) is necessary to reproduce this behavior. This just so happens to align the start of the comment range for the prop assignment in foo.ts (63) one character past the start of the B assignment's comment range in bar.ts (62).

Strangely, if I remove a single character from line 5 (so that both comment range starts are perfectly aligned), the comment above B.prop is omitted, but the others remain:

export declare const x: {
    A: {
        /** Comment for `B` */
        prop: string;
    };
    /** Comment for `B` */
    B: {
        prop: string;
    };
    C: {
        /** Comment for `B` */
        prop: string;
    };
};

Any other changes to the number of characters preceding the object literal expression in foo.ts results in the expected bar.d.ts:

export declare const x: {
    A: {
        prop: string;
    };
    /** Comment for `B` */
    B: {
        prop: string;
    };
    C: {
        prop: string;
    };
};
🙂 Expected behavior

Ideally, the emitted bar.d.ts declaration file should include the correct comments for all property signatures:

export declare const x: {
    A: {
        /** Comment for `prop` */
        prop: string;
    };
    /** Comment for `B` */
    B: {
        /** Comment for `prop` */
        prop: string;
    };
    C: {
        /** Comment for `prop` */
        prop: string;
    };
};

But I understand from the FAQ that might have a performance impact. In that case, I would expect the bar.d.ts file to consistently omit comments for type literals defined outside bar.ts:

export declare const x: {
    A: {
        prop: string;
    };
    /** Comment for `B` */
    B: {
        prop: string;
    };
    C: {
        prop: string;
    };
};

In other words, the compiler should handle this corner case where comment positions just happen to align across multiple files. The current behavior can lead to weird declaration file inconsistencies across platforms (due to CR characters potentially aligning/mis-aligning comment ranges on Windows only).

Additional information about the issue

A couple other observations:

  • This is only reproducible with a JSDoc-style comment block (/**) in bar.ts
  • Changing or removing the comment for prop on line 7 has no effect on the emitted bar.d.ts
  • This appears to happen anytime a type defined in foo.ts is substituted for an expression in bar.ts (and comment ranges align). For example, here's another repro showing similar behavior when bar.ts includes calls to a function (returning an object literal) defined in foo.ts, and yet another where said function returns a class expression instead.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.