fix(client-generator-ts): define TransactionClient as shared PrismaClientBase to speed up type inference - #30053
fix(client-generator-ts): define TransactionClient as shared PrismaClientBase to speed up type inference#30053arab971 wants to merge 4 commits into
Conversation
…ientBase Previously the generated TransactionClient was defined as Omit<PrismaClient, ITXClientDenyList>, forcing TypeScript to instantiate the Omit mapped type over the entire PrismaClient surface whenever a TransactionClient or a union of PrismaClient and TransactionClient had a member resolved. With 40+ models this made type inference slow (see prisma#28967). Introduce a shared PrismaClientBase interface carrying the members common to PrismaClient and TransactionClient: the symbol index signature, raw query methods, model delegates and (for SQL providers) the $transaction methods. PrismaClient extends PrismaClientBase and adds the members denied inside interactive transactions ($connect, $disconnect, $on, $extends; $transaction for MongoDB). TransactionClient is now a plain alias to PrismaClientBase, so member resolution is a direct reference instead of a mapped type instantiation. Type inference for both interactive $transaction callbacks and TransactionClient | PrismaClient unions improves (huge-schema tsc instantiations: 26124 -> 26128 for a full client usage, 26512 -> 26120 for interactive tx, 26931 -> 26680 for the union; explicit-union model access drops ~56.8ms to ~23.6-58ms). A type-benchmark regression guard is added under huge-schema/transaction-client.bench.ts; under the previous Omit design the tx ?? client bench grows +28% and the explicit union bench +77%, both over the 20% threshold. Type-level behavioral tests are extended in packages/client/src/__tests__/types/$transaction to assert that PrismaClient is assignable to TransactionClient while the interactive transaction callback argument is not assignable to PrismaClient and does not expose the denied members.
…nsactionClient denied members The tsd assignability tests in packages/client/src/__tests__/types only exercise the legacy JS generator (prisma-client-js). Add a guard that runs against the TS generator (prisma-client) output this change modifies: huge-schema/transaction-client-types.ts asserts PrismaClient is assignable to TransactionClient while TransactionClient is not assignable to PrismaClient and does not expose $connect, $disconnect, $on or $extends (asserted via used @ts-expect-error directives; any leak back makes the directive unused and tsc fails with TS2578). The type-benchmark harness now runs tsc --noEmit per test directory, so the guard executes in CI next to the attest instantiation benchmark.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
|
Hi @wmadden — would you mind taking a look at this when you have a moment? It targets the type-inference slowdown from #28967 ( It includes a repro with before/after measurements (tsc instantiations + language-server timing), a type-benchmark guard pinned at 638/522 instantiations (the old |
|
CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set |
Problem
Type inference is too slow when
PrismaClientis used together withTransactionClient, e.g. the commonconst db = tx ?? clientpattern in interactive transactions. The generatedTransactionClientwas defined as:Resolving any member on
TransactionClient(or on a unionPrisma.TransactionClient | PrismaClient) forced TypeScript to instantiate theOmitmapped type over the entirePrismaClientsurface. With 40+ models this dominates inference time and makes editor completions sluggish.Step 1 — Reproduction
Measured against a generated client for a 49-model schema (
packages/type-benchmark-tests/huge-schema):PrismaClientBase)$transactionTransactionClient | PrismaClientunionStep 2 — Fix
Introduce a shared
PrismaClientBase<LogOpts, OmitOpts, ExtArgs>interface carrying the members common toPrismaClientandTransactionClient: the[K: symbol]index signature, raw query methods ($executeRaw*,$queryRaw*,$runCommandRaw), model delegates, and (for SQL providers) the$transactionmethods (batch + interactive).PrismaClientnowextends PrismaClientBaseand adds only the members denied inside interactive transactions ($connect,$disconnect,$on,$extends; plus$transactionfor MongoDB, preserving the previous deny-list semantics where Mongo'sTransactionClientexcluded$transaction).TransactionClientis now a plain alias toPrismaClientBase, so member resolution is a direct reference instead of a mapped-type instantiation.DefaultPrismaClientremainsPrismaClient.The legacy JS generator (
client-generator-js,prisma-client-jsprovider) still uses theOmitpattern and is intentionally out of scope for this change (follow-up).Step 3 — Regression guards
Performance (attest):
packages/type-benchmark-tests/huge-schema/transaction-client.bench.ts(49-model schema, where the gap widens with model count):tx ?? client— 638 instantiationsTransactionClient | PrismaClient— 522 instantiationsVerified against the previous design:
tx ?? clientgrows +28% and the explicit-union bench +77%, both above attest's 20% threshold — reintroducing theOmitpattern fails CI.Behavior (types):
packages/client/src/__tests__/types/$transaction(extended): interactive$transactioncallback usage, plus tsd assertions —PrismaClientis assignable toTransactionClient, whileTransactionClientis not assignable toPrismaClientand does not expose$connect/$disconnect/$on/$use/$extends.packages/type-benchmark-tests/huge-schema/transaction-client-types.ts(new): the same negative-assignability contract asserted against the TS generator output (the tsd tests above target the JS generator), via used@ts-expect-errordirectives — if a denied member leaks back, the directive becomes unused andtscfails with TS2578. The type-benchmark harness now runstsc --noEmitper test directory so this guard executes in CI next to the attest benchmark.Tests
@prisma/client-generator-tsgenerator tests: 39/39 passclient-generator-ts): clean$transactionjest type test: passeshuge-schema/transaction-client.bench.tsand the per-directorytsctypecheck pass (the twoclient-options.bench.tsbenches fail only on attest baseline noise in the sandbox environment; the files are untouched by this change)Closes Type inference is too slow when
PrismaClientis union withTransactionClient#28967