Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@Zaczero
Copy link

@Zaczero Zaczero commented Dec 22, 2025

This PR was written primarily by GPT 5.2.

Summary

Unknown branch propagation

If node might be unknown, node.parentNode must include unknown.

interface Node { parentNode: Node | null }

declare let node: Node | unknown
node.parentNode
// safe approximation: (Node | null) | unknown

Without this, inference can become too narrow by effectively “dropping” the unknown branch

Expression-derived union branches

If a union variant is still an inferred expression type (Biome’s internal “type-of-expression”), flattening obj.member can re-enter expression reasoning in a way that’s unstable for patterns like:

while (node) node = node.parentNode

So we conservatively refuse to flatten in that case (leave it unflattened for this pass).

Probably fixes #8204.

Verified fix on https://github.com/openstreetmap-ng/openstreetmap-ng/blob/main/app/views/lib/standard-form.ts

Test Plan

cargo test -p biome_js_type_info --test flattening
cargo test -p biome_module_graph test_type_flattening_does_not_explode_on_recursive_parent_element_pattern

@changeset-bot
Copy link

changeset-bot bot commented Dec 22, 2025

🦋 Changeset detected

Latest commit: 58a37de

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Project Area: project L-JavaScript Language: JavaScript and super languages A-Type-Inference Area: type inference labels Dec 22, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

Walkthrough

This change hardens static-member flattening for union types by switching from a pre-collected variant mapping to an iterative per-variant resolution loop. It skips global-undefined variants, pushes unknown on resolution failures, aborts if a variant is a typeof-expression, and preserves unknown variants in resulting unions. Tests were added to ensure unknown variants are preserved and typeof-expression variants are not flattened. Two new public diagnostic types (JsModuleInfoDiagnostic, ModuleDiagnostic) and a test preventing types-limit explosions for a recursive parentElement pattern were also introduced.

Possibly related PRs

Suggested reviewers

  • dyc3
  • ematipico
  • arendjr

Pre-merge checks and finishing touches

✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description thoroughly explains the two key fixes: unknown branch propagation and expression-derived union branches, with concrete examples and test plan.
Linked Issues check ✅ Passed The PR directly addresses issue #8204 by fixing the type explosion bug in union static-member flattening that occurred with recursive DOM traversal patterns.
Out of Scope Changes check ✅ Passed All changes align with fixing union static-member flattening: core logic changes, test additions, and new diagnostic type exports are all in scope.
Title check ✅ Passed The title accurately describes the main change: hardening union static-member type flattening logic to handle edge cases with unknown and expression variants.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

This patch tightens the behavior for TypeofExpression::StaticMember on unions:
- If a union variant is unknown, propagate unknown into the result.
- Resolve the member across union variants directly (no distribution into nested inferred expressions).
- If a union variant is still an inferred expression type (TypeofExpression), refuse to flatten to avoid re-entering expression-based inference that can lead to runaway growth.
@Zaczero Zaczero force-pushed the zaczero/temporary-tortoise branch from 575d3e4 to cdc136e Compare December 22, 2025 03:05
@dyc3
Copy link
Contributor

dyc3 commented Dec 22, 2025

At a glance, this looks a lot like #8536, which is already merged.

@Zaczero
Copy link
Author

Zaczero commented Dec 22, 2025

@dyc3 At a glance, yes, and it's because I started working on it yesterday and finished it up after a sleep (at which point this PR was made). But it has 3 benefits in addition to that PR: it avoids vec allocation, correctly handles unknown whereas 8536 stripped unknowns, and it fixes the recursion whereas 8536 did not. The added tests fail on 8536.

@dyc3 dyc3 requested review from a team December 22, 2025 03:24
@codspeed-hq
Copy link

codspeed-hq bot commented Dec 22, 2025

CodSpeed Performance Report

Merging #8546 will not alter performance

Comparing Zaczero:zaczero/temporary-tortoise (58a37de) with main (6b09620)1

Summary

✅ 58 untouched
⏩ 95 skipped2

Footnotes

  1. No successful run was found on main (e8cafcc) during the generation of this report, so 6b09620 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

  2. 95 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Tests were already added, so one isn't needed anymore

Comment on lines +85 to +120
#[test]
fn infer_flattened_type_of_static_member_on_union_does_not_flatten_expression_variants() {
const CODE: &str = r#"interface Node {
parentNode: Node | null;
}
node.parentNode"#;

let root = parse_ts(CODE);
let interface_decl = get_interface_declaration(&root);
let mut resolver = GlobalsResolver::default();
let interface_ty =
TypeData::from_ts_interface_declaration(&mut resolver, ScopeId::GLOBAL, &interface_decl)
.expect("interface must be inferred");
resolver.run_inference();

let interface_ref = resolver.reference_to_owned_data(interface_ty);
let expression_variant =
resolver.reference_to_owned_data(TypeData::TypeofExpression(Box::new(
TypeofExpression::StaticMember(TypeofStaticMemberExpression {
object: TypeReference::unknown(),
member: Text::new_static("parentNode"),
}),
)));
let union_ty = TypeData::union_of(&resolver, [interface_ref, expression_variant].into());

let expr = get_expression(&root);
let mut resolver = HardcodedSymbolResolver::new("node", union_ty, resolver);
let expr_ty = TypeData::from_any_js_expression(&mut resolver, ScopeId::GLOBAL, &expr);
let expr_ty = expr_ty.inferred(&mut resolver);

assert!(
matches!(&expr_ty, TypeData::TypeofExpression(expr) if matches!(expr.as_ref(), TypeofExpression::StaticMember(_))),
"expected to keep typeof-expression unflattened, got: {expr_ty}",
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test isn't needed anymore because it was already added

@dyc3 dyc3 changed the title fix(analyze/types): union static-member flattening refactor(analyze/types): union static-member flattening Dec 22, 2025
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changeset is also not needed, since it was added in the other PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Project Area: project A-Type-Inference Area: type inference L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 Detected case of unusually large amount of types

3 participants