Skip to content

Fix strict select subset typing under TS6#2720

Open
olup wants to merge 2 commits into
zenstackhq:devfrom
olup:codex/fix-select-subset-ts6
Open

Fix strict select subset typing under TS6#2720
olup wants to merge 2 commits into
zenstackhq:devfrom
olup:codex/fix-select-subset-ts6

Conversation

@olup

@olup olup commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes a TypeScript 6 / tsgo typing gap where direct client calls accepted unknown nested keys in select objects, even though the explicit args type rejected them.

The issue was reported in #2719.

What changed

  • Tightened SelectSubset for select, include, and omit properties so nested keys are checked against the contextual argument shape.
  • Added a regression typecheck for unknown select fields on direct client calls.
  • Marked existing slicing / unsupported-field runtime validation cases with @ts-expect-error, matching their comments and the now-stricter compile-time behavior.

Validation

  • pnpm --filter @zenstackhq/orm build
  • pnpm --filter e2e test:typecheck

Created by Codex on human request.

Summary by CodeRabbit

Release Notes

  • Refactor

    • Strengthened ORM TypeScript typing for select, include, omit, and related query arguments so invalid fields are rejected more precisely, including stricter nested shape validation and null/undefined preservation.
  • Tests

    • Expanded end-to-end type-check coverage with additional ts-expect-error assertions for top-level and deeply nested select/include/omit, plus negative cases for unsupported where/orderBy usage.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8216d092-ee74-45a7-a8e0-8302842d2eab

📥 Commits

Reviewing files that changed from the base of the PR and between 31aa89e and 371687e.

📒 Files selected for processing (4)
  • packages/orm/src/client/crud-types.ts
  • tests/e2e/orm/client-api/slicing.test.ts
  • tests/e2e/orm/client-api/unsupported.test.ts
  • tests/e2e/orm/schemas/typing/typecheck.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/e2e/orm/client-api/slicing.test.ts

📝 Walkthrough

Walkthrough

SelectSubset<T, U> is rewritten to enforce stricter query-argument shaping, including nested selection rules and where/orderBy/cursor handling. The e2e type tests add // @ts-expect-error`` checks for invalid field access and invalid nested query shapes.

Changes

SelectSubset strict typing and test coverage

Layer / File(s) Summary
Strict helpers and SelectSubset rewrite
packages/orm/src/client/crud-types.ts
Adds helper conditional types for nullish preservation and extra-key rejection, applies strict shaping to nested selection and query-argument properties, and replaces SelectSubset with the stricter transformation.
TypeScript error assertions in e2e tests
tests/e2e/orm/schemas/typing/typecheck.ts, tests/e2e/orm/client-api/slicing.test.ts, tests/e2e/orm/client-api/unsupported.test.ts
Adds // @ts-expect-error`` assertions for invalid field access, invalid select and nested `include` or `omit` shapes, excluded model selections, unsupported fields, and invalid nested `where` and `orderBy` paths.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐇 I hopped through select with a careful eye,
Extra keys blinked once, then said goodbye.
include and omit now snugly stay,
While where and orderBy know the way.
The compiler thumps: “Nope!” with a grin,
And rabbit-safe types hop neatly in.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: stricter SelectSubset typing to fix TS6 select subset behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ymc9

ymc9 commented Jun 25, 2026

Copy link
Copy Markdown
Member

Hi @olup , thanks for making the fix! I'm not sure if it's by design, but I believe the improved EPC check only works for top-level select/omit/include. Adding recursion will probably incur significant type-checking cost, so I think it's a valid trade-off. Looking good to me.

ymc9
ymc9 previously approved these changes Jun 25, 2026
@olup

olup commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Great ! I'll have a quick look this morning on recursion/performance.

@olup olup force-pushed the codex/fix-select-subset-ts6 branch from 3816813 to f5fa064 Compare June 25, 2026 07:46
@olup

olup commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

(message produces by codex under human supervision)

I updated the branch to address the recursion/performance concern with a different approach.

Instead of using a fixed recursion depth, the new version only applies strict checking along the user-provided selection tree, and only for select, include, and omit keys. When a nested relation argument is reached, it checks the keys at that FindArgs level, but it does not recursively exact-check arbitrary sub-objects like where, orderBy, or cursor; those continue to be handled by their existing types. So the recursion is bounded by the shape of the user query, not by an arbitrary depth constant, and it stays focused on selection/include/omit exactness.

I also compared this with Drizzle's model. Drizzle's KnownKeysOnly is shallow, while the recursive behavior mainly comes from DBQueryConfig recursively typing relation with configs. A purely shallow KnownKeysOnly-style helper was not enough for ZenStack's FindArgs shape, so this patch keeps a small targeted recursive helper around selection keys only.

Quick local typechecking perf check:

  • Command: cd tests/e2e && /usr/bin/time -p npx tsc --noEmit --extendedDiagnostics --pretty false
  • Ran 3 times on the current PR baseline and 3 times on this update.

Average e2e results:

Metric Baseline Updated Delta
Types 300,193 314,965 +4.9%
Instantiations 2,475,523 2,582,921 +4.3%
Memory used ~998 MB ~1009 MB +1.2%
Check time 10.64s 10.69s roughly unchanged
Wall time 15.69s 15.55s roughly unchanged/noise

I also checked packages/orm directly with tsc --extendedDiagnostics; the structural metrics were essentially flat there (Instantiations: 730,826 -> 730,833).

Validation run locally:

  • pnpm --filter @zenstackhq/orm build
  • pnpm --filter e2e test:typecheck
  • npx -p typescript@5.9.3 tsc --noEmit -p tsconfig.json --pretty false from tests/e2e

@olup olup marked this pull request as draft June 25, 2026 08:18
@olup

olup commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Checking a couple of details on the where queries before reopening this one

@olup olup force-pushed the codex/fix-select-subset-ts6 branch from 31aa89e to 371687e Compare June 25, 2026 08:25
@olup

olup commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

(message produced by codex under human supervision)

Small follow-up: I also checked the where/orderBy concern explicitly.

The selection recursion helper intentionally does not recursively walk arbitrary sub-objects like scalar filter payloads or sort payloads. However, where, orderBy, and cursor should still be checked against the queried model's existing argument types at the level where they appear.

I added explicit type tests for:

  • invalid root where field
  • invalid root orderBy field
  • invalid nested relation where field
  • invalid nested relation orderBy field
  • unsupported field in orderBy

Implementation-wise, the patch now applies shallow exactness to where, orderBy, and cursor at each query-args level, while keeping the deeper recursive exactness focused on select, include, and omit. It still does not recursively exact-check arbitrary filter/operator internals beyond the existing WhereInput/OrderBy types.

Validation rerun:

  • pnpm --filter @zenstackhq/orm build
  • pnpm --filter e2e test:typecheck
  • npx -p typescript@5.9.3 tsc --noEmit -p tsconfig.json --pretty false from tests/e2e

@olup olup marked this pull request as ready for review June 25, 2026 08:29
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.

2 participants