Optimize distributive conditional type instantiation for trivial extends types#2711
Open
DukeDeSouth wants to merge 1 commit intomicrosoft:mainfrom
Open
Conversation
…nds types When distributing a conditional type T extends U ? X : Y over a union, if the extends type U resolves to 'never', 'any', or 'unknown', the branch outcome is trivially determinable for all union members without running the full getConditionalType machinery per member. - When U is 'never': T extends never is false for all non-never T, so we directly apply the false branch to each member. - When U is 'any' or 'unknown': T extends any/unknown is true for all T, so we directly apply the true branch to each member. This skips expensive per-member operations including type instantiation, permissive instantiation, and structural comparison (isTypeAssignableTo) that are redundant when the branch is predetermined. The optimization primarily benefits patterns like Exclude<BigUnion, never> (commonly triggered by Omit<T, never>) where hundreds of union members would each trigger the same branch outcome. In complex generic patterns with large React prop types (250+ properties), this significantly reduces the total instantiation count, helping avoid false positive TS2589 errors. Safety guards: only applies when !forConstraint, no infer type parameters, and distribution type is a union. Falls through to the original path for all other cases. Fixes microsoft/TypeScript#34933 Co-authored-by: Cursor <cursoragent@cursor.com>
Author
|
@microsoft-github-policy-service agree |
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.
Human View
Summary
Adds a fast path in
getConditionalTypeInstantiationfor distributive conditional types where the extends type resolves tonever,any, orunknown. In these cases, the branch outcome is trivially determinable for all union members, so we skip the expensive per-membergetConditionalTypecall (which involves type instantiation, permissive instantiation, and structural comparison) and directly apply the predetermined branch.The Problem
When distributing a conditional type like
Exclude<BigUnion, never>(=T extends never ? never : T) over a large union (e.g., React HTML props with 250+ members), the current code callsgetConditionalTypefor each member of the union. Each call:instantiateTypecall)instantiateTypecall)isTypeAssignableTostructural comparisoninstantiateTypecall)For
extends never, every member goes to the false branch — all this work is redundant. In complex HOC patterns (withRouter + generic enhance + React props), multiple layers ofOmit/Pick/Excludecompound this, potentially exhausting the 5MinstantiationCountbudget and producing a false positive TS2589 error.The Fix
Before distributing over union members, resolve the extends type. If it's:
never: All non-never members go to false branch → apply false branch directlyany/unknown: All members go to true branch → apply true branch directlySafety guards (to prevent false negatives on genuinely infinite types):
!forConstraint(constraint resolution has different semantics)len(root.inferTypeParameters) == 0(infer types need inference logic)getConditionalTypepath for all other casesTesting
limitDeepInstantiationsstill correctly errors on genuinely infinite typesrecursiveConditionalTypes/recursiveConditionalCrashtests passconditionalType*andmappedType*tests passFixes microsoft/TypeScript#34933
Related: #988, #2609
AI View (DCCE Protocol v1.0)
Metadata
AI Contribution Summary
Verification Steps Performed
Human Review Guidance
Made with M7 Cursor