rushstack icon indicating copy to clipboard operation
rushstack copied to clipboard

[api-extractor] import() syntax generates invalid dts rollup result sometimes

Open adventure-yunfei opened this issue 6 months ago • 1 comments

Summary

when using together with manual import, import() syntax may generate invalid dts rollup result.

Repro steps

run dts rollup with following dts input file:

import { FooModule } from 'foo';
export interface Foo {
    prop: FooModule.FooClass;
    prop_importType: import('foo').FooModule.FooClass;
}
export {}

Expected result: Generate dts rollup result:

import { FooModule } from 'foo';
export declare interface Foo {
  prop: FooModule.FooClass;
  prop_importType: FooModule.FooClass;
}
export { }

Actual result:

import { FooModule } from 'foo';
export declare interface Foo {
  prop: FooModule.FooClass;
  prop_importType: FooModule;  // this should be FooModule.FooClass
}
export { }

Details

Found the reason after debug. When fetchAstImport, the result is cached by key from AstImport.getKey. But AstImport with AstImportKind.ImportType type generates the same key with other type. So when both types exist in the dts file, they are all resolved to the same AstImport instance. And the import type mismatching will cause error. e.g. when emitting dts rollup, only AstImoprt with ImportType is handled for import() syntax (in modifyImportTypeSpan).

Maybe we should ensure that the same cache key always refers to the same result.

Standard questions

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

Question Answer
@microsoft/api-extractor version? 7.52.11
Operating system? Mac
API Extractor scenario? rollups (.d.ts)
Would you consider contributing a PR? Yes
TypeScript compiler version? 5.8.2
Node.js version (node -v)? v16.20.0

adventure-yunfei avatar Aug 27 '25 03:08 adventure-yunfei

seems there are many edge cases that are not handled correctly.

  1. Missing typeof decorator: input:
    export interface Foo {
      prop_importType: typeof import('foo').FooClass;
    }
    export {}
    
    output:
    import { FooClass } from 'foo';
    export declare interface Foo {
      prop_importType: FooClass;  // should be: typeof FooClass
    }
    export { }
    
  2. Generate invalid dts rollup for import() with no qulifier: input:
    export interface Foo {
      prop_importType: typeof import('foo');
    }
    export {}
    
    output:
    import { foo } from 'foo';  // should be: import * as foo from 'foo';
    export declare interface Foo {
      prop_importType: foo;
    }
    export { }
    
  3. Won't handle for namespace: input:
    // index.d.ts
    export interface Foo {
      prop_importType: import('./foo').FooNS.FooClass;
    }
    export {}
    
    // foo.d.ts
    export declare namespace FooNS {
      export interface FooClass {}
    }
    export {}
    
    output:
    export declare interface Foo {
      prop_importType: import('./foo').FooNS.FooClass;
    }
    export { }
    
  4. May include external package declarations (if import('./foo').X.Y.Z resolves to external package declarations).
  5. Will break for const foo: typeof import('./foo').

adventure-yunfei avatar Aug 28 '25 02:08 adventure-yunfei