Skip to content

Partial fix: strip required from anyOf/oneOf schema items to reduce TS2615 circular reference errors#3694

Draft
Copilot wants to merge 7 commits into
masterfrom
copilot/fix-circular-reference-error
Draft

Partial fix: strip required from anyOf/oneOf schema items to reduce TS2615 circular reference errors#3694
Copilot wants to merge 7 commits into
masterfrom
copilot/fix-circular-reference-error

Conversation

Copilot AI commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

Problem

OpenAPI schemas that use anyOf/oneOf to model circular (self-referential) types caused TypeScript error TS2615 when using OASModel, OASOutput, or the feTS client. The root cause: json-schema-to-ts's MergeSubSchema<{}, T> produces an anonymous Omit<> intersection type, breaking TypeScript's cycle-detection and causing TS2615.

Solution

Added to packages/fets/src/types.ts:

  • HasCircularAnyOfRef<T> – detects schemas with anyOf/oneOf items carrying the $id marker (added by ResolveRef for $ref-expanded schemas)
  • DirectType<T> (exported) – a named recursive generic type alias that TypeScript evaluates lazily, the same way type Tree<T> = { value: T; children: Tree<T>[] } works; prevents TS2615 by keeping recursive calls as the same named type alias instantiation instead of an anonymous mapped type
  • FromSchema<T> updated – routes schemas where HasCircularAnyOfRef<T> is true through DirectType<T>; falls back to FromSchemaOriginal for all other schemas

Progress

  • Investigate: FixSchemaWithId alone didn't fix TS2615 — 3 errors remain in anyof-circular-ref-test.ts
  • Add HasCircularAnyOfRef<T>, ContainsAnyOfWithId<T>, AnyHasId<Items> helper types
  • Add DirectType<T>, DirectUnionType<Items>, DirectIntersectionType<Items> types
  • Modify FromSchema<T> to use DirectType<T> when HasCircularAnyOfRef<T> is true
  • All 3 TS2615 errors in anyof-circular-ref-test.ts resolved
  • circular-ref-test.ts, circular-ref-array-test.ts, mutual-circular-ref-test.ts, oas-test.ts still pass
  • Code review and CodeQL validation pass

💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

Copilot AI linked an issue Mar 30, 2026 that may be closed by this pull request
Copilot AI and others added 2 commits March 30, 2026 07:37
…duce TS2615 circular reference errors

Agent-Logs-Url: https://github.com/ardatan/feTS/sessions/c2d8eec9-96bd-4af6-a1c7-65ddf2986403

Co-authored-by: ardatan <20847995+ardatan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix circular reference error in OpenAPI spec Partial fix: strip required from anyOf/oneOf schema items to reduce TS2615 circular reference errors Mar 30, 2026
Copilot AI requested a review from ardatan March 30, 2026 08:06
@github-actions

Copy link
Copy Markdown
Contributor

💻 Website Preview

The latest changes are available as preview in: https://3e60071d.fets.pages.dev

@github-actions

Copy link
Copy Markdown
Contributor

✅ Benchmark Results

     ✓ no_errors{server:node-http,mode:no-schema}
     ✓ expected_result{server:node-http,mode:no-schema}
     ✓ no_errors{server:node-http,mode:json-schema}
     ✓ expected_result{server:node-http,mode:json-schema}
     ✓ no_errors{server:uWebSockets,mode:no-schema}
     ✓ expected_result{server:uWebSockets,mode:no-schema}
     ✓ no_errors{server:uWebSockets,mode:json-schema}
     ✓ expected_result{server:uWebSockets,mode:json-schema}

     checks......................................: 100.00% ✓ 971066      ✗ 0     
     data_received...............................: 88 MB   735 kB/s
     data_sent...................................: 62 MB   514 kB/s
     http_req_blocked............................: avg=1.45µs   min=681ns    med=1.21µs   max=1.13ms   p(90)=1.9µs    p(95)=2.82µs  
     http_req_connecting.........................: avg=1ns      min=0s       med=0s       max=157.22µs p(90)=0s       p(95)=0s      
     http_req_duration...........................: avg=169.46µs min=89.13µs  med=159.45µs max=14.74ms  p(90)=189.92µs p(95)=201.38µs
       { expected_response:true }................: avg=169.46µs min=89.13µs  med=159.45µs max=14.74ms  p(90)=189.92µs p(95)=201.38µs
     ✓ { server:node-http,mode:json-schema }.....: avg=179.66µs min=113.03µs med=169.59µs max=11.66ms  p(90)=198.27µs p(95)=208.89µs
     ✓ { server:node-http,mode:no-schema }.......: avg=171.85µs min=103.36µs med=159.19µs max=14.74ms  p(90)=190.01µs p(95)=203.73µs
     ✓ { server:uWebSockets,mode:json-schema }...: avg=166.67µs min=92.82µs  med=157.89µs max=12.14ms  p(90)=186.35µs p(95)=196.17µs
     ✓ { server:uWebSockets,mode:no-schema }.....: avg=160.45µs min=89.13µs  med=151.01µs max=14.39ms  p(90)=180.31µs p(95)=191.21µs
     http_req_failed.............................: 0.00%   ✓ 0           ✗ 485533
     http_req_receiving..........................: avg=23µs     min=8.34µs   med=21.26µs  max=3.14ms   p(90)=31.49µs  p(95)=35.23µs 
     http_req_sending............................: avg=8.03µs   min=4.02µs   med=7.19µs   max=2.03ms   p(90)=11.8µs   p(95)=13.84µs 
     http_req_tls_handshaking....................: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting............................: avg=138.42µs min=66.5µs   med=128.5µs  max=14.67ms  p(90)=156.08µs p(95)=166.18µs
     http_reqs...................................: 485533  4046.039387/s
     iteration_duration..........................: avg=242.5µs  min=145.88µs med=231.06µs max=14.9ms   p(90)=267.53µs p(95)=282.95µs
     iterations..................................: 485533  4046.039387/s
     vus.........................................: 1       min=1         max=1   
     vus_max.....................................: 2       min=2         max=2   

@ardatan

ardatan commented May 4, 2026

Copy link
Copy Markdown
Owner

@copilot take a look at this one
#3689
See if it fixes the issue, and resolve the conflicts too. Create a changeset as well

Copilot AI and others added 2 commits May 4, 2026 17:37

Copilot AI commented May 4, 2026

Copy link
Copy Markdown
Contributor Author

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.clickhouse.cloud
    • Triggering command: /usr/local/bin/node node /home/REDACTED/work/feTS/feTS/node_modules/.bin/ts-node scripts/download-oas.ts (dns block)
    • Triggering command: /usr/local/bin/node node /home/REDACTED/work/feTS/feTS/node_modules/.bin/ts-node scripts/download-oas.ts git conf�� get --global /home/REDACTED/.local/bin/git HooksPath (dns block)
    • Triggering command: /usr/local/bin/node node /home/REDACTED/work/feTS/feTS/node_modules/.bin/ts-node scripts/download-oas.ts git conf�� get --global p/bin/node user.email (dns block)
  • api.navitia.io
    • Triggering command: /usr/local/bin/node node /home/REDACTED/work/feTS/feTS/node_modules/.bin/ts-node scripts/download-oas.ts (dns block)
    • Triggering command: /usr/local/bin/node node /home/REDACTED/work/feTS/feTS/node_modules/.bin/ts-node scripts/download-oas.ts git _mod�� --global user.email ndor/bin/bash (dns block)
    • Triggering command: /usr/local/bin/node node /home/REDACTED/work/feTS/feTS/node_modules/.bin/ts-node scripts/download-oas.ts git conf�� get --global p/bin/node gpg.program (dns block)
  • developer.spotify.com
    • Triggering command: /usr/local/bin/node node /home/REDACTED/work/feTS/feTS/node_modules/.bin/ts-node scripts/download-oas.ts 53 -j ACCEPT (dns block)
    • Triggering command: /usr/local/bin/node node /home/REDACTED/work/feTS/feTS/node_modules/.bin/ts-node scripts/download-oas.ts (dns block)
    • Triggering command: /usr/local/bin/node node /home/REDACTED/work/feTS/feTS/node_modules/.bin/ts-node scripts/download-oas.ts git _mod�� -b copilot/fix-circular-reference-error ndor/bin/bash --depth 4 REDACTED git conf�� get --global node_modules/@npmcli/run-script/lib/node-gyp-bin/sh commit.gpgsign (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI and others added 2 commits May 4, 2026 18:46
…DirectType

Add HasCircularAnyOfRef<T> detection and DirectType<T> recursive type alias
so that FromSchema<T> uses a cycle-safe computation path for schemas containing
circular anyOf/oneOf $ref items, avoiding TS2615 entirely.

- HasCircularAnyOfRef<T> detects whether any property's anyOf/oneOf items
  carry the $id marker added by ResolveRef (i.e. they are $ref-expanded).
- DirectType<T> is a named recursive generic type alias that TypeScript
  evaluates lazily (same as `type Tree<T> = { value: T; children: Tree<T>[] }`),
  so when the recursion leads back to DirectType<MutableResolvedFG> the
  compiler defers instead of triggering TS2615.
- FromSchema<T> now checks HasCircularAnyOfRef<T> first and uses DirectType
  for circular anyOf schemas, falling back to FromSchemaOriginal for all others.

Agent-Logs-Url: https://github.com/ardatan/feTS/sessions/2b95da3e-6eba-4d79-9e20-a3b303b0f41a

Co-authored-by: ardatan <20847995+ardatan@users.noreply.github.com>
Copilot finished work on behalf of ardatan May 4, 2026 18:53
@scamden

scamden commented May 19, 2026

Copy link
Copy Markdown

@ardatan any luck?

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.

Circular reference error in openapi spec

3 participants