Skip to content

[api-extractor] Lose name when export with declare var #3353

Open

Description

@qmhc

Summary

If using declare var, the name will be lost in the fianl rollup file.

// const.d.ts
declare const _default: { a: string }
export default _default;

// var.d.ts
declare var _default: { a: string }
export default _default;

// index.d.ts
import Const from './const'
import Var from './var';
export { Const, Var }
// rollup.d.ts
export declare const Const: { a: string };
export declare var _default: { a: string };
export { }

Repro steps

  1. Clone https://github.com/qmhc/api-extractor-issue.git
  2. Install then run pnpm run build:dts
  3. Check index.d.ts and rollup.d.ts in 'dist'

Expected result: export declare var Var: { a: string }

Actual result: export declare var _default: { a: string }

When more than one file includes declare var _default, the final rollup file will have errors.

// index.d.ts
import Var from './var';
import Var1 from './var1';
export { Var, Var1 }

// rollup.d.ts
export declare var _default: { a: string }; // get errors
export declare var _default: { b: string }; // get errors
export { }

Details

I'm not sure if this is a known limitation or a bug.

Standard questions

Please answer these questions to help us investigate your issue more quickly:

Question Answer
@microsoft/api-extractor version? Latest
Operating system? Windows
API Extractor scenario? rollups
Would you consider contributing a PR? No
TypeScript compiler version? 4.5.5
Node.js version (node -v)? 16.13.1

Activity

  1. added
    bugSomething isn't working as intended
    help wantedIf you're looking to contribute, this issue is a good place to start!
    effort: easyProbably a quick fix. Want to contribute? :-)
    on Apr 14, 2022
  2. octogonz commented on Apr 14, 2022

    @octogonz
    Collaborator

    This is a bug. We should fix it.

    That said, export default is not a recommended practice:

    • There is no validation of the exported name, so if you accidentally write import Button from './Checkbox'; then the compiler cannot detect this mistake. Your code may even compile successfully if the Button and Checkbox APIs are similar enough, which is very confusing.
    • Default exports encourage the consumer to choose a different name (e.g. import Manager from './RequestManager';) which means that if someone does a Find-in-Files or bulk Search+Replace for RequestManager, they can no longer have high confidence that it will find them all
    • Lastly, default exports seem to provide NO compelling advantage over a named export import { Button } from './Button';. As far as I can tell, there really is no reason to use export default, except for things that are truly anonymous such as importing a JSON resource.

    Thus, I'm really curious why your project wouldn't simply use import { Var1 } from './var1';

  3. added
    repro confirmedThe issue comments included repro instructions, and the maintainers reproduced the problem
    on Apr 14, 2022
  4. octogonz commented on Apr 15, 2022

    @octogonz
    Collaborator

    Rocco (@qmhc) thanks for the great repro. 👍 I debugged it, and the problem is here:

    public fetchReferencedAstEntity(
    symbol: ts.Symbol,
    referringModuleIsExternal: boolean
    ): AstEntity | undefined {
    // eslint-disable-next-line no-bitwise
    if ((symbol.flags & ts.SymbolFlags.FunctionScopedVariable) !== 0) {
    // If a symbol refers back to part of its own definition, don't follow that rabbit hole
    // Example:
    //
    // function f(x: number): typeof x {
    // return 123;
    // }
    return undefined;
    }

    The SymbolFlags.FunctionScopedVariable test is supposed to catch for example x in this declaration:

    export declare function function f(x: number): typeof x;

    ....but instead it is incorrectly detecting Var in your repro:

    export declare var Var: { a: string }

    This seems like it might be a compiler bug -- I'm not sure I would consider Var to be a "function-scoped variable."

    If I comment out the line return undefined; then your .d.ts rollup gets generated correctly. However, probably the x case would be broken.

    I'm on vacation so I can't investigate this further right now. But if we can figure out a good test to distinguish these two cases, it should be pretty easy for someone to make a PR.

    Ron Buckton (@rbuckton) do you have any suggestions?

  5. qmhc commented on Apr 15, 2022

    @qmhc
    Author

    Pete Gonzalez (@octogonz) I quite agree with you for:

    export default is not a recommended practice.

    Once I wrote a vue component with jsx (for some tests), after the declaration file emitted I found it by accident.

    Normally we should use tsx for type support instead of jsx (and when using tsx, this case will not appear).

    I hesitated whether to report, because this may not a correct usage.

    // comp.jsx
    import { defineComponent } from 'vue'
    
    export default defineComponent({
      setup() {
        return () => (
          <div class='jsx-test'></div>
        )
      }
    })
    
    // index.ts
    import Comp from './comp.jsx'
    export { Comp }
    
    // comp.d.ts
    declare var _default: import("vue").DefineComponent<{}, () => JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, import("vue").EmitsOptions, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
    export default _default;
  6. octogonz commented on Apr 15, 2022

    @octogonz
    Collaborator

    Even so, we should still try to fix this bug. 🙂

  7. moved this from Needs triage to AE/AD in Bug Triageon Feb 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working as intendedeffort: easyProbably a quick fix. Want to contribute? :-)help wantedIf you're looking to contribute, this issue is a good place to start!repro confirmedThe issue comments included repro instructions, and the maintainers reproduced the problem

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions