Skip to content

fix: resolve Data type names from the SDK signature for digit-leading operationIds - #214

Merged
7nohe merged 5 commits into
mainfrom
claude/github-issue-213-8ff28f
Aug 11, 2026
Merged

fix: resolve Data type names from the SDK signature for digit-leading operationIds#214
7nohe merged 5 commits into
mainfrom
claude/github-issue-213-8ff28f

Conversation

@7nohe

@7nohe 7nohe commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Closes #213

Problem

When an operationId starts with a digit (e.g. 123numericLead), hey-api names the SDK function and its Data type differently:

  • function: _123NumericLead (prefixed — identifiers cannot start with a digit)
  • type: NumericLeadData (digits dropped)

This codegen derived the Data type name from the method name (${capitalizedMethodName}Data_123NumericLeadData), which does not exist. Two symptoms followed:

  1. Pagination silently disabledgetPaginatableMethods keyed its map by a name derived from the Data type, so the lookup missed and the operation got no infinite hooks despite a valid page parameter.
  2. Generated code did not typecheck — the unknown fallback emitted Options<unknown, true>, producing 6 × TS2344 ('unknown' does not satisfy the constraint 'TDataShape').

Fix

Read the Data type name from the SDK function's own signature — the first type argument of Options<XData, ThrowOnError> in sdk.gen.ts is the authoritative answer and is already in the AST that parseOperations.mts walks:

  • parseOperations extracts dataTypeName from the signature and carries it on OperationInfo.
  • getPaginatableMethods now keys its map by the Data type name itself, so the pagination lookup can never diverge from the resolved type.
  • All builders (buildQueryHooks, buildMutationHooks, buildCommon, buildQueryOptions) consume op.dataTypeName via a single getDataTypeName(op) helper instead of re-deriving the name. Builders whose ctx parameter became unused (buildQueryKeyFn, buildQueryOptionsFn, buildPrefetchFn, buildEnsureQueryDataFn) had it removed.

The unknown fallback now only triggers when the SDK signature exposes no Data type at all.

Tests

  • New tests/inputs/digit-leading.yaml fixture generating the real hey-api output (_123NumericLead / NumericLeadData), asserting the divergence is resolved and pagination is detected.
  • The four Options<unknown, true> assertions that pinned the broken output are rewritten: the fallback is now pinned to the no-Data-type-in-signature case, and new digit-leading cases assert the correct type is emitted.
  • The chore: remove unreachable unknown-Data fallback from infinite builders #209 guard test ("Data type in modelNames for every paginatable operation") is kept green, now asserting via op.dataTypeName.

Verified end-to-end with the repro from the issue: the generated output contains use_123NumericLeadInfinite casting to Options<NumericLeadData, true>, and tsc --noEmit over the generated files exits 0.

Follow-up: review pass (b2b2356, b053bf1)

A multi-angle cleanup review over the diff surfaced one sibling defect and several simplifications, applied in two follow-up commits:

  • Error types had the same Digit-leading operationId silently disables pagination and emits non-compiling code #213 divergence: getErrorType derived ${capitalizedMethodName}Error, so digit-leading operationIds silently degraded TError to unknown. The stem now comes from dataTypeName (hey-api mints both names from the same stem — pinned by a new fixture assertion).
  • getDataTypeName and the previously duplicated getErrorType now live in a neutral src/tsmorph/operationNames.mts, removing the buildCommon → buildQueryHooks import edge.
  • getDataTypeNameFromSignature takes the already-computed options parameter instead of re-walking the arrow function; an inert getShortType call was dropped.
  • The naming rule is stated once on OperationInfo.dataTypeName; duplicated comment blocks and test assertions made inert by the refactor were removed.
  • New end-to-end test: the digit-leading fixture (now with an error response and a POST operation) runs through the full createSource pipeline and the generated output is compiled with zero TypeScript diagnostics.

… operationIds

When an operationId starts with a digit, hey-api prefixes the SDK
function name (`_123NumericLead`) but strips the digits from the Data
type (`NumericLeadData`). Deriving the type name from the method name
(`${capitalizedMethodName}Data`) therefore missed, which silently
disabled pagination (no infinite hooks despite a valid page parameter)
and emitted `Options<unknown, true>`, failing to compile with TS2344.

Read the Data type name from the SDK function's own
`Options<XData, ThrowOnError>` parameter instead — the signature is the
authoritative source — and key the paginatable-methods map by that name
so the pagination lookup can never diverge from it.

The `unknown` fallback now only triggers when the SDK signature exposes
no Data type at all; the tests pinning the previously broken output are
rewritten accordingly, and the #209 guard invariant (every paginatable
operation has its Data type in modelNames) is preserved via
`op.dataTypeName`.

Closes #213
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openapi-react-query-codegen Ready Ready Preview Aug 11, 2026 2:14pm

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🟢 Lines 99.2% (🎯 95%) 497 / 501
🟢 Statements 98.09% (🎯 95%) 515 / 525
🟢 Functions 99.26% (🎯 95%) 135 / 136
🟢 Branches 91.66% (🎯 90%) 209 / 228
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/parseOperations.mts 88.31% 77.58% 94.44% 95.65% 32, 48, 60, 85, 86, 105, 117, 120, 129
src/types.mts 100% 100% 100% 100%
src/tsmorph/buildCommon.mts 100% 100% 100% 100%
src/tsmorph/buildMutationHooks.mts 100% 100% 100% 100%
src/tsmorph/buildQueryHooks.mts 100% 95.83% 100% 100%
src/tsmorph/buildQueryOptions.mts 100% 100% 100% 100%
src/tsmorph/generateFiles.mts 100% 100% 100% 100%
src/tsmorph/operationNames.mts 100% 100% 100% 100%
Generated in workflow #464 for commit 3587031 by the Vitest Coverage Report Action

Apply cleanup findings from the parallel review pass:

- Move getDataTypeName to a neutral operationNames.mts module and
  consolidate the two duplicated getErrorType copies there, removing the
  buildCommon -> buildQueryHooks import edge.
- Derive the Error type stem from dataTypeName instead of the method
  name — the same #213 divergence applied to hey-api's Error types,
  silently degrading TError to unknown for digit-leading operationIds.
- Pass the already-computed options parameter into
  getDataTypeNameFromSignature instead of re-walking the arrow function,
  and drop the inert getShortType call on written type-argument text.
- State the naming rule once on OperationInfo.dataTypeName (hey-api-owned
  names are read from the signature; self-minted names may derive from
  the method name) and trim the comment blocks that restated it.
- Tests: build the digit-leading mock via spread, drop modelNames Data
  entries and assertions made inert by the refactor, and extend the
  digit-leading fixture with an error response and a POST operation to
  pin the Error-type stem rule end to end.
Address the review gap flagged by both the altitude and Codex passes:
the digit-leading regression was pinned only at the OperationInfo level,
so nothing proved the parser and generator together emit infinite hooks
and output that typechecks. Run the full createSource pipeline over the
digit-leading fixture, organize imports the way generate.mts does, and
assert zero TypeScript diagnostics over the generated queries.
Type-checking the generated output against the real TanStack Query types
exceeds vitest's default 5s timeout on CI runners.
The suite runs real codegen (hey-api generation, TypeScript programs),
which is multi-second work; under CI runner contention the 5s default
flaked on the pre-existing bundler-resolution test as well. Replace the
per-test override with a global timeout.
@7nohe
7nohe merged commit 8c63eae into main Aug 11, 2026
5 checks passed
@7nohe
7nohe deleted the claude/github-issue-213-8ff28f branch August 11, 2026 14:17
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.

Digit-leading operationId silently disables pagination and emits non-compiling code

1 participant