-
Notifications
You must be signed in to change notification settings - Fork 604
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] API report shows spurious diffs because compiler emits inferred types inconsistently #1958
Comments
@dmichon-msft This repro doesn't seem to work.
index.ts looks like this -- I don't see export * from './A';
export * from './B';
export * from './C'; The README.md in the repo instead says:
Add what property? What should I expect to observe exactly? Please give explicit instructions. |
Oops, forgot to push when I simplified the repro.
and
Further investigation has concluded that this repro is specifically dependent on inferring a type that contains a union. Providing an explicit typedef solves the underlying instability issue. |
(BTW I thought your ESLint rules require explicit type declarations?) That said, if the compiler's output depends non-deterministically on the order in which an incremental build occurs -- that seems like a design flaw in the incremental build feature. Implementing a deterministic algorithm might be difficult work. But that doesn't make it acceptable to remove the guarantee of determinism. If we remove that guarantee, then essentially ANY type expression can shuffle around arbitrarily during compilation (not just unions), which would imply that API Extractor has the burden of normalizing every detail of every type expression. This seems very much like the compiler's responsibility. |
To clarify a bit: Since there are many equivalent ways to infer a type, it's unsurprising that a type expression might shuffle around a bit due to modifications of its inputs. That part doesn't bother me, or seem like much of a problem for API Extractor's reporting. The problematic part of this repro is:
Are we really considering this to be a correctly functioning incremental build implementation? CC @RyanCavanaugh @DanielRosenwasser for thoughts |
Yes, this is functioning correctly. The types are equivalent and we have never instructed anyone to take a dependency on the text representation of a union type, nor would we. |
@RyanCavanaugh Then we need to normalize the expression afterwards. (This scenario is not about type equivalence, but rather about a text file being processed for reporting and documentation purposes.) Question: If the compiler now produces random outputs when invoked on the same input file, how random is it? Is it merely union type members shuffling around? Can entire expressions be replaced with a semantically equivalent expression? Can functions be reordered? Can line numbers be added/removed that will shift the source maps? Basically we need to know what kind of normalization is needed. Today API Extractor makes efforts to faithfully preserve stylistic aspects of its input as much as possible. |
Relevant things that could happen to my knowledge:
|
Does this only happen with inferred types? Will explicitly declared types always get emitted consistently? |
Yeah, if we're able to re-use the existing text of a node, we always will |
For this, it sounds pretty straightforward to normalize by sorting them alphabetically. (Might even be cheap and useful for the compiler to do that.)
🤔 Does a normal form even exist for this problem? Or suppose we wanted to keep the previously reported expression if nothing has changed. Is there even a well defined test to determine if two type expressions are exactly equivalent? |
Possible solutions:
@iclanton do you have thoughts? |
They generate non-deterministic types microsoft/rushstack#1958
* disable incremental builds for public type generation. They generate non-deterministic types microsoft/rushstack#1958 * do not infer public types
* disable incremental builds for public type generation. They generate non-deterministic types microsoft/rushstack#1958 * do not infer public types
* disable incremental builds for public type generation. They generate non-deterministic types microsoft/rushstack#1958 * do not infer public types
This needs the COmmonJS builds to not be incremental See microsoft/rushstack#1958 (comment) for details. JIRA: RAIL-1815
This needs the CommonJS builds to not be incremental See microsoft/rushstack#1958 (comment) for details. JIRA: RAIL-1815
We are currently using |
Using TS project references and composite builds makes this issue for more pronounced. Union ordering and object properties shuffle ordering depending on build order leaning to false positive diffs |
Is this a feature or a bug?
Please describe the actual behavior.
TypeScript does not define the output order of the constituents of a union type. As a result of this, the output from API-extractor is inconsistent between fresh builds and incremental builds. From discussion with the TypeScript team, this is related to the order of loading of types and whether or not certain types need to be instantiated to type check the set of files that were built during the compilation.
The default sort order for union type constituents is based on the internal debug id, which is assigned when the type is instantiated.
If the issue is a bug, how can we reproduce it? Please provide detailed steps and include a GitHub branch if applicable. Your issue will get resolved faster if you can make it easy to investigate.
https://github.com/dmichon-msft/union-type-order-demo
Repro steps:
incrementStep1
inindex.ts
. Runnpm run build
. Observe order ofexport const test
declaration.incrementStep2
inB.ts
. Runnpm run build
. Observe order ofexport const test
declaration.The order will change depending on which files needed recompilation.
The order directly matches that which is output in lib/index.d.ts, which is what changes.
What is the expected behavior?
Since API extractor is concerned with substantive changes, it should define a standard (potentially configurable, but not required) sort order for the constituents of union types, regardless of the order output by TypeScript. This should be similar to the sorting of interface members that api-extractor already performs.
If this is a bug, please provide the tool version, Node.js version, and OS.
Edited 6/23@1:29 PM: simplified repro.
The text was updated successfully, but these errors were encountered: