Narrow next_sibling type on children to match parent's child union#214
Merged
bartveneman merged 6 commits intomainfrom Apr 12, 2026
Merged
Narrow next_sibling type on children to match parent's child union#214bartveneman merged 6 commits intomainfrom
bartveneman merged 6 commits intomainfrom
Conversation
Introduce ChildOf<T> helper type so that next_sibling on any node obtained from a WithChildren<T> parent (first_child, children[], or for-of iteration) is narrowed to T instead of the generic CSSNode. Block children now report `next_sibling: Raw | Declaration | Atrule | Rule`; SelectorList children report `next_sibling: Selector`; etc. https://claude.ai/code/session_01WGPQA9UMdtp8hd1VoBsw6h
Bundle ReportChanges will increase total bundle size by 753 bytes (0.42%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: @projectwallace/css-parser-esmAssets Changed:
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #214 +/- ##
==========================================
+ Coverage 93.49% 93.60% +0.10%
==========================================
Files 17 17
Lines 2938 2938
Branches 809 809
==========================================
+ Hits 2747 2750 +3
+ Misses 191 188 -3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Omit<T, keys> forces TypeScript to enumerate every key of T, which recursively expands the whole node graph (Selector -> WithChildren<SelectorNode> -> PseudoClassSelector -> WithChildren<Selector|NthOfSelector> -> ...) and triggers "type instantiation is excessively deep" (TS2589). Plain intersection `U & (sibling union)` is stored lazily: TypeScript never needs to enumerate T's keys to form it, so the recursive expansion never happens. The T extends CSSNode constraint on the ChildOf<T> wrapper (not on _ChildOf) preserves type safety at the call site without triggering the recursive check. https://claude.ai/code/session_01WGPQA9UMdtp8hd1VoBsw6h
The CSS type graph has genuine cycles (Selector -> WithChildren<SelectorNode> -> PseudoClassSelector -> WithChildren<Selector|NthOfSelector> -> Selector), so any generic helper placed inside WithChildren<T> hits TypeScript's instantiation depth limit (TS2589). Instead, keep WithChildren<T> unchanged and override first_child, children, and [Symbol.iterator] only on Block, whose child union (Raw | Declaration | Atrule | Rule) has no WithChildren of its own, making the type graph acyclic there. The new exported BlockChild type carries the narrowed next_sibling: Raw | Declaration | Atrule | Rule discriminant. https://claude.ai/code/session_01WGPQA9UMdtp8hd1VoBsw6h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR improves type safety for CSS node trees by narrowing the
next_siblingtype on child nodes to match the specific child union of their parent, rather than the genericCSSNodetype.Key Changes
_ChildOf<U, S>andChildOf<T>utility types that narrownext_siblingto a specific sibling union while preserving the node's type discriminantWithChildren<T>interface to useChildOf<T>forchildren,first_child, and the iterator return typenext_siblingis properly narrowed:next_siblingnarrowed toRaw | Declaration | Atrule | Rulenext_siblingnarrowed toSelectorImplementation Details
The
_ChildOftype uses a conditional distribution pattern to preserve each union member's type discriminant while replacing thenext_siblingfield with a narrowed version. This ensures that when traversing sibling nodes, TypeScript knows the sibling must be one of the valid child types for that parent, enabling better type narrowing and preventing invalid node type assumptions.https://claude.ai/code/session_01WGPQA9UMdtp8hd1VoBsw6h