-
Notifications
You must be signed in to change notification settings - Fork 15
Fix: DSL Type Inference for .array() and .map() Methods #248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
|
View your CI Pipeline Execution ↗ for commit f2a7dc5
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting
| # Run tsc directly on the file without --project flag | ||
| # This allows TS2578 errors (unused @ts-expect-error) to be detected | ||
| OUTPUT=$(pnpm tsc --noEmit "$file" 2>&1) | ||
| TSC_EXIT=$? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preserve tsconfig when running per-file tsc checks
Removing the temporary config and calling pnpm tsc --noEmit "$file" causes the compiler to run with default options instead of those from tsconfig.typecheck.json. That means path aliases, esModuleInterop, moduleResolution, and other required settings are ignored. Any test file that imports packages through aliases or relies on non-default options now fails the strict check even when the project-wide pass succeeds (for example Pkgs/core/__tests__/types/PgflowSqlClient.test-d.ts now reports Cannot find module '@pgflow/dsl'). This produces false failures and masks the real intent of the second pass (detecting unused @ts-expect-error). The command still needs to use the project configuration while checking files individually.
Useful? React with 👍 / 👎.
3d797ba to
edc0b5e
Compare
| finalValue: input.normalStep.doubledValueArray.length, | ||
| })); | ||
| })) | ||
| .step({ slug: 'undefined' }, () => undefined); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change introduces a type error by adding a step that returns undefined, which violates the newly added RejectUndefined type constraint. According to the type definition, step handlers must return a value other than undefined (though null is permitted). To fix this, modify the handler to return a valid value such as null or an object.
| .step({ slug: 'undefined' }, () => undefined); | |
| .step({ slug: 'undefined' }, () => null); |
Spotted by Graphite Agent
Is this helpful? React 👍 or 👎 to let us know.
pkgs/dsl/src/dsl.ts
Outdated
| map<Slug extends string, THandler extends (...args: any[]) => any>( | ||
| opts: Simplify<{ slug: Slug extends keyof Steps ? never : Slug } & StepRuntimeOptions>, | ||
| handler: TFlowInput extends readonly (infer Item)[] | ||
| ? THandler & ((item: Item, context: BaseContext & TContext) => Json | Promise<Json>) | ||
| ? RejectUndefined<THandler> | ||
| : never |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation of the map function overload loses type information for the handler parameters. While adding the RejectUndefined constraint is good, it should preserve the parameter types:
handler: TFlowInput extends readonly (infer Item)[]
? RejectUndefined<(item: Item, context: BaseContext & TContext) => Json | Promise<Json>>
: neverThis ensures both the parameter types are preserved and the return type is properly constrained, maintaining type safety throughout the map operation.
| map<Slug extends string, THandler extends (...args: any[]) => any>( | |
| opts: Simplify<{ slug: Slug extends keyof Steps ? never : Slug } & StepRuntimeOptions>, | |
| handler: TFlowInput extends readonly (infer Item)[] | |
| ? THandler & ((item: Item, context: BaseContext & TContext) => Json | Promise<Json>) | |
| ? RejectUndefined<THandler> | |
| : never | |
| map<Slug extends string, THandler extends (...args: any[]) => any>( | |
| opts: Simplify<{ slug: Slug extends keyof Steps ? never : Slug } & StepRuntimeOptions>, | |
| handler: TFlowInput extends readonly (infer Item)[] | |
| ? RejectUndefined<(item: Item, context: BaseContext & TContext) => Json | Promise<Json>> | |
| : never |
Spotted by Graphite Agent
Is this helpful? React 👍 or 👎 to let us know.
9ddceb6 to
25868a2
Compare
53fdbe8 to
93999c5
Compare
pkgs/dsl/__tests__/types/README.md
Outdated
| - **After TypeScript version upgrades** | ||
| - **After Vitest updates** | ||
| - **After modifying tsconfig files** | ||
| - **After changing typecheck-strict.sh script** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation in this file references typecheck-strict.sh, but this script has been renamed to typecheck-ts2578.sh in this PR. The documentation should be updated to reference the new script name to maintain consistency and avoid confusion for developers following these instructions.
| - **After changing typecheck-strict.sh script** | |
| - **After changing typecheck-ts2578.sh script** | |
Spotted by Graphite Agent
Is this helpful? React 👍 or 👎 to let us know.
32f65e0 to
af4e0d5
Compare
…uns in strict mode then - Replaces temporary tsconfig creation with direct tsc invocation on each file - Enables detection of unused @ts-expect-error errors during strict type checks - Improves script clarity and reliability by removing unnecessary config file handling
af4e0d5 to
f2a7dc5
Compare
🔍 Preview Deployment: Website✅ Deployment successful! 🔗 Preview URL: https://pr-248.pgflow.pages.dev 📝 Details:
_Last updated: _ |
🔍 Preview Deployment: Playground✅ Deployment successful! 🔗 Preview URL: https://pr-248--pgflow-demo.netlify.app 📝 Details:
_Last updated: _ |
Merge activity
|
## Problem
TypeScript failed to automatically infer handler parameter types in `.array()` and `.map()` methods, forcing users to add manual type annotations:
```
// Required manual annotations (BAD)
.array({ slug: 'fetch_users' }, async ({ run }: { run: { userIds: string[] } }) => {
return run.userIds.map((id: string) => ({ id, name: `User_${id}` }));
})
.map({ slug: 'add_timestamps', array: 'fetch_users' }, (user: { id: string; name: string }) => ({
...user,
createdAt: new Date().toISOString(),
}))
```
## Root Cause
Complex conditional types on handler **parameters** prevented TypeScript from inferring the `THandler` type parameter:
```
// ❌ Prevented inference
handler: ValidateHandlerReturn<THandler, readonly any[]>
handler: TFlowInput extends readonly (infer Item)[]
? THandler & ((item: Item, ...) => ...)
: never
```
## Solution
Moved all type constraints from parameter types to **type parameter constraints**:
```
// ✅ Enables inference
THandler extends (...) => readonly any[] | Promise<readonly any[]>
handler: THandler
THandler extends TFlowInput extends readonly (infer Item)[]
? (item: Item, ...) => Json | Promise<Json>
: never
handler: THandler
```
### Additional Fixes
**Client exhaustiveness checks**: Replaced verbose unused variable pattern with cleaner `satisfies never`:
```
// Before
const _exhaustivenessCheck: never = event;
// After
event satisfies never;
```
## Results
✅ **Type inference works automatically** - no manual annotations needed
✅ **All 111 DSL type tests pass**
✅ **All package typechecks pass** (dsl, core, client, edge-worker)
✅ **Cleaner exhaustiveness checks** - no unused variable warnings
## Type System Changes
- `.array()`: Handler constraint moved to type parameter
- `.map()` (root): Handler constraint moved to type parameter
- `.map()` (dependent): Handler constraint moved to type parameter
- Removed unused `ValidateHandlerReturn` utility type
These changes maintain the same runtime behavior and type safety while enabling proper type inference.

Problem
TypeScript failed to automatically infer handler parameter types in
.array()and.map()methods, forcing users to add manual type annotations:Root Cause
Complex conditional types on handler parameters prevented TypeScript from inferring the
THandlertype parameter:Solution
Moved all type constraints from parameter types to type parameter constraints:
Additional Fixes
Client exhaustiveness checks: Replaced verbose unused variable pattern with cleaner
satisfies never:Results
✅ Type inference works automatically - no manual annotations needed
✅ All 111 DSL type tests pass
✅ All package typechecks pass (dsl, core, client, edge-worker)
✅ Cleaner exhaustiveness checks - no unused variable warnings
Type System Changes
.array(): Handler constraint moved to type parameter.map()(root): Handler constraint moved to type parameter.map()(dependent): Handler constraint moved to type parameterValidateHandlerReturnutility typeThese changes maintain the same runtime behavior and type safety while enabling proper type inference.