Skip to content

perf(bindx-client): declare TEntity invariant on selection builder methods - #150

Merged
matej21 merged 1 commit into
mainfrom
perf/selection-builder-entity-variance
Oct 24, 2026
Merged

matej21 merged 1 commit into
mainfrom
perf/selection-builder-entity-variance

Conversation

@matej21

@matej21 matej21 commented Oct 24, 2026

Copy link
Copy Markdown
Member

Summary

ScalarMethod, HasOneMethod and HasManyMethod now declare in out TEntity.

These interfaces return SelectionBuilder again, so TypeScript measures TEntity's variance through a cycle with the -? mapped type in SelectionBuilderMethods. Under TypeScript 7.1 the measurement comes out unmeasurable, so every comparison between two builders falls back to a structural one. That costs instantiations quadratic in the entity's field count, and on large schemas it hits TS2589: Type instantiation is excessively deep. Reduced repro and bisect: microsoft/TypeScript#64950.

TypeScript 7.0 was only fast because it measured TEntity as independent through the cycle, which is unsound. With the same declarations, 7.0.2 accepts Method<{ a: 1 }, 'a', never> as Method<{ a: 1; b: 1 }, 'a', never>. Declaring the variance avoids measuring it at all.

  • out TEntity is rejected (TS2636): TEntity is not covariant here.
  • in out is not checked by TypeScript and is the conservative choice.
  • TSelected and THasManyParams need no annotation.

Verification

  • bun run typecheck, bun run test (2198 pass) and eslint on the changed file all pass.

  • On a large downstream app (~2,100 files), checking its main package single-threaded:

    before after
    TypeScript 7.1.0-dev.20261024.1 182 s, 18.8 GB, 77 errors 24 s, 1.9 GB, 0 errors
    TypeScript 7.0.2 36 s, 2.8 GB, 0 errors 36 s, 2.7 GB, 0 errors

    No new errors on 7.0.2 or 7.1 anywhere in that app. The Rust and Bun checkers that follow 7.1 also go from failing to 0 errors.

  • There is no regression test: bindx compiles with TypeScript 5.9, which does not show the slowdown.

🤖 Generated with Claude Code

…thods

ScalarMethod, HasOneMethod and HasManyMethod reference SelectionBuilder
again, so TypeScript measures TEntity's variance through a cycle with
the `-?` mapped type in SelectionBuilderMethods. Under TypeScript 7.1
that measurement comes out unmeasurable, which forces a structural
comparison of every builder: the cost grows with the square of the
entity's field count and hits TS2589 on large schemas
(microsoft/TypeScript#64950). TypeScript 7.0 was only fast because it
measured TEntity as independent through the cycle, which is unsound.

`in out` states the variance instead of measuring it. TSelected and
THasManyParams need no annotation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@matej21
matej21 merged commit e6fd9bd into main Oct 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant