Microsoft/TypeScript

Conditional type eagerly calculated to be invariant breaks referential transparency

Open

#62798 opened on Nov 23, 2025

View on GitHub
 (5 comments) (0 reactions) (0 assignees)TypeScript (48,455 stars) (6,726 forks)batch import
BugCursed?Domain: check: Variance RelationshipsHelp Wanted

Description

🔎 Search Terms

Conditional types, Invariance, Referential transparency

🕗 Version & Regression Information

Tried with v5.9.3

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.9.3#code/C4TwDgpgBAYgPAFQHxQLxQN5QG4EMA2ArhAFxQJQQAewEAdgCYDOUA9gEYBWEAxsFAH4oAawghWAM3JQyFAL4AoBaEhQASgEY0sOBjkpqtRi3hZcZJsABOASzoBzKPsFQARAAtW+Zq5luGrBAsdKz8nt6uCgD0UVBxAHoCSirQMLjapvrK4KnsGbpQ5lCWtg5OSMk56gBMGemG9MyweUIeXj5+rgFBUCFh7ZExcVCJCkA

💻 Code

type F<T> = { value: T extends object ? keyof T : T }

type R1 = F<{}> extends F<{ a: string }> ? "holds" : "does not hold"
//   ^? "does not hold"

type Fa = F<{}>
type Fb = F<{ a: string }>

type R2 = Fa extends Fb ? "holds" : "does not hold"
//   ^? "holds"

🙁 Actual behavior

R1 computes to "does not hold"

🙂 Expected behavior

R1 computes to "holds"

Additional information about the issue

I'm assuming typescript eagerly calculates F to be invariant and then instead of checking the subtype relation on resolved types it calculates subtype relation of the parameters to F. (This would perhaps have made sense if F was explicitly annotated with in out T.)

Contributor guide