-
Notifications
You must be signed in to change notification settings - Fork 607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[api-extractor] import() types are not handled correctly in .d.ts rollups #1050
Comments
export declare const ɵdefineDirective: import("./nested").CssSelector[]; Oh man... do people really do this? :-P Is there a practical reason why this shouldn't be replaced by a conventional named import? API Extractor should add support for this in theory, but I've been trying to triage people's feature requests into 3 categories:
Inline |
@pgonzal, the lazy import is not written in code, but emitted by the ts compiler. Example having the following export enum SelectorFlags {
NOT = 0b0001
}
export type CssSelector = (string | SelectorFlags)[];
export function foo(): CssSelector {
return;
} index.ts import { foo } from "./nested";
export const x = foo(); index.d.ts export declare const x: (string | import("./nested").SelectorFlags)[]; While this can be trivial to fix and not rely on type inference, I'd at least expect that the dts bundler to emit some sort of error to warn the user. |
Ahh thanks for clarifying. This should be pretty easy to fix I think. |
BTW the "easy" fix is to report an error when this is encountered. Properly rewiring the |
Hi! Author of rollup-plugin-dts here :-) I just did a short comparison of some edge-cases between api-extractor and my plugin and noticed that your specific usecase might be supported by my plugin without a problem. |
I believe we're also encountering that bug when trying to build https://github.com/async-library/react-async with pika, which internally uses api-extractor. Typescript creates type files with import type references like Could you take another look at this please? |
@phryneas Can you share a repro branch that I could use to investigate/debug this? |
As far as the repro above, what's happening is that the type of index.ts import { foo } from "./nested";
export const x = foo(); Newer versions of the TypeScript compiler (>= 3.2) have started emitting the inferred type into the output, like this: index.d.ts export declare const x: (string | import("./nested").SelectorFlags)[]; The ugly-looking index.d.ts import { CssSelector } from "./nested";
export declare const x: CssSelector; In any case, our own projects would never encounter this issue, because we would never declare a public API without a type annotation. Our lint rules require the source code to look like this: index.ts import { foo, CssSelector } from "./nested";
export const x: CssSelector = foo(); // <--- properly typed variable Compared to this, the original index.ts is arguably helping someone bang out code faster, at the expense of making the result more difficult for other people read and understand. Seems like great evidence in favor of this lint rule heheh. API Extractor cannot emit @phryneas In the meantime, are you able to work around your issue by adding explicit type annotations for the relevant APIs? If not, then we'd definitely give this fix a higher priority. |
You pointed me in the right direction to circumvent this bug - and pinpoint it in our case. In this case, we have this method signature: export const IfFulfilled = <T extends {}>({
children,
persist,
state = {} as any,
}: {
children?: FulfilledChildren<T>
persist?: boolean
state: AsyncState<T>
}) => (
<>{state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null}</>
) Now, export declare type AsyncState<T, S extends AbstractState<T> = AbstractState<T>> = /* ... */ So the usage of And that leads to the whole problem. Now, the solution is almost stupid: I only import But still, I guess that there are many more cases like this, where |
For references, this blog article describes some more use cases and motivation for " |
Things break on this PR: ScaleLeap/amazon-mws-api-sdk#47 Things is, TS itself is generating this type. I have not written that manually. export declare class Sellers {
private httpClient;
constructor(httpClient: HttpClient);
listMarketplaceParticipations(): Promise<[MarketplaceParticipations, RequestMeta]>;
listMarketplaceParticipationsByNextToken(nextToken: NextToken<'ListMarketplaceParticipations'>): Promise<[MarketplaceParticipations, RequestMeta]>;
getServiceStatus(): Promise<[{
Status: import("../parsing").ServiceStatus;
Timestamp: string;
}, RequestMeta]>;
} |
@moltar just as a workaround, do an |
After implementing the error on import() type encounter, I thought about the next milestone: adding support for import() types. The plan is to follow the same approach as rollup-plugin-dts.
First, But I got stuck. The dtsRollup can only match Identifiers to AstImports, and import() types don't provide any Identifier. The core of this functionality is implemented at Do two import() type statements with the same module path share the same Symbol? In that case AstSymbolTable can be modified to match Symbols to AstEntities. If this is implemented, identifiers could be matched to AstEntities using this scheme "Identifier -> Symbol -> AstEntity", or AstSymbolTable could have 2 tables: "Identifier -> AstEntity" and "Symbol -> AstEntity". |
I would not rely on that assumption. For example in #1874 (comment) we recently found a violation of a reasonable assumption about (However if you follow the import to the imported module, that
Hmm... This is actually the part that needs an equivalence relation, to avoid creating duplicate rushstack/apps/api-extractor/src/analyzer/ExportAnalyzer.ts Lines 713 to 714 in 3bd05a0
...with: rushstack/apps/api-extractor/src/analyzer/AstImport.ts Lines 119 to 128 in aac158f
I bet this same idea will work for
The generator's lookup here is very dumb actually. It is essentially saying "Hey, remember when we already analyzed this thing in AstSymbolTable._analyzeChildTree()? Give me back the entity that we found then." So the mapping really should happen in If there is no
This might work, but it makes me uneasy. The idea behind Another issue is that the mappings can have tricky edge cases. For example, "displaced symbols" get filtered out during the mapping. We should be able to forget about all that nonsense after Again it might work. But I'd have to think about it to convince myself that it is correct for all cases. I'm not good at keeping lots of stuff in my head, so these proofs can be time-consuming (which is why #1796 is taking so long). |
That said, your overall idea sounds correct and rather straightforward to implement. I had assumed it would require some kind of architectural change. It would be so awesome to add support for I'm also reachable in the API Extractor chat room if you have more questions. |
- Workaround for microsoft/rushstack#1050
Thanks for your explanation @octogonz, it has been of great help. Now it's working, take a look at my branch import_type_support_simplicity. Summary of how import() types are converted to star imports
I also thought about another strategy: leave external references untouched as import() nodes if possible. The idea is to improve accuracy with respect to the original source. This attempt sits in branch import_type_support_accuracy. It works, but there are some challenges to overcome. It is built on top of a new
|
@octogonz is anyone currently working on a fix for this? |
@lifeiscontent I believe that PR #1916 is a partial fix for this problem. Could you try that branch and see if it works for your scenario? We've started a new effort to get these old PRs finally merged, so if PR #1916 works for you, we could prioritize that one. |
@octogonz is there a way I can npm install that PR? |
Instructions for compiling and running the branch are here: If that doesn't work, tomorrow I can publish a PR build for you. |
@lifeiscontent We've published the PR branch as |
|
Schema uses an imported type, fixed by microsoft/rushstack#1050
|
package.json{
"name": "@microsoft/api-extractor",
"version": "7.13.2-pr1916.0",
"description": "Analyze the exported API for a TypeScript library and generate reviews, documentation, and .d.ts rollups",
"keywords": [
"typescript",
"API",
"JSDoc",
"AEDoc",
"TSDoc",
"generate",
"documentation",
"declaration",
"dts",
".d.ts",
"rollup",
"bundle",
"compiler",
"alpha",
"beta"
],
"repository": {
"type": "git",
"url": "https://github.com/microsoft/rushstack/tree/master/apps/api-extractor"
},
"homepage": "https://api-extractor.com",
"main": "lib/index.js",
"typings": "dist/rollup.d.ts",
"bin": {
"api-extractor": "./bin/api-extractor"
},
"license": "MIT",
"scripts": {
"build": "heft test --clean"
},
"dependencies": {
"@microsoft/api-extractor-model": "workspace:*",
"@microsoft/tsdoc": "0.12.24",
"@rushstack/node-core-library": "workspace:*",
"@rushstack/rig-package": "workspace:*",
"@rushstack/ts-command-line": "workspace:*",
"colors": "~1.2.1",
"lodash": "~4.17.15",
"resolve": "~1.17.0",
"semver": "~7.3.0",
"source-map": "~0.6.1",
"typescript": "~4.1.3"
},
"devDependencies": {
"@rushstack/eslint-config": "workspace:*",
"@rushstack/heft": "0.23.1",
"@rushstack/heft-node-rig": "0.2.0",
"@types/heft-jest": "1.0.1",
"@types/lodash": "4.14.116",
"@types/node": "10.17.13",
"@types/resolve": "1.17.1",
"@types/semver": "~7.3.1"
}
} |
@jsamr Yeah I ran into the same issue, ended up cloning the repo locally, fixing the package.json, and installing as a local dependency. |
installable with Yarn1/2/3:
|
Fixed by PR #1916 from @javier-garcia-meteologica 🎈 Released with API Extractor 7.18.0 |
That did the trick, thanks so much! 🙏 |
microsoft/rushstack#1050 has been solved and therefore we no longer need this workaround. PR Close #42798
Lazy types are not being rewired when using dtsRollup.
index.d.ts
nested.d.ts
output
I'd expect that the lazy type is resolved and appended to the file and the import is removed.
expected output
version: 7.0.11
The text was updated successfully, but these errors were encountered: