typebox-codegen icon indicating copy to clipboard operation
typebox-codegen copied to clipboard

Circular type support, or at least detection?

Open csorfab opened this issue 3 months ago • 2 comments

Hi @sinclairzx81 ! First of all, thank you for creating and maintaining this project, it helped me a lot through the typed-openapi lib.

TL;DR: TypescriptToModel.Generate silently produces incorrect output from circular types. I'd be interested in creating a PR to detect circular types and fail/warn instead.


Yesterday I ran into a problem while working with an openAPI schema that, unbeknownst to me, contained circles - it started generating weird outputs, with some types missing altogether, other types unnecessarily expanded in Type_path_to_field formats, whereas previously it worked flawlessly.

It took me 4 hours of digging into typebox and typebox-codegen code, console.logging my way through to understand what's happening, to realize that we had a circle, and that it caused some types during TypescriptToModel.Exports to be evaluated to undefined, which made the wrapping Type invalid, which made an IsSchema check fail, that made TypescriptToModel.Types to start namespacing subfields, and cause the main type to be omitted from the output.

Here's a minimal repro for what I'm talking about:

import { ModelToTypeScript, TypeScriptToModel } from "@sinclair/typebox-codegen";

const model = TypeScriptToModel.Generate(`
export type A = {
    b: B[];
    c: {
        d: string;
    }
}

export type B = {
    a: A[];
}

`);

const code = ModelToTypeScript.Generate(model);

console.log(code, "\n\n", model);

this generates the following output (I include only the generated TS types for clarity's sake - the model already only contains these types, so the problem lies with TypescriptToModel):

export type A_properties_c = {
  d: string
}

export type B_properties_a_items_properties_c = {
  d: string
}

Both main types (A, B) are omitted without warning, and strange scoped types are generated for subfields.

I understand that it's difficult to represent circular types with Typebox, and even more difficult to write a parser that handles them in a general way, but I think you will agree that producing incorrect output without errors or warning is undesirable behavior.

Would you be interested in a PR that would add checking for circular type definitions while walking the TS AST, and throw an error if it detects one? It would be an interesting challenge to me, and it might spare some poor folks from going through the digging I had to go through :D

csorfab avatar Oct 30 '25 10:10 csorfab

@csorfab Hi,

I understand that it's difficult to represent circular types with Typebox, and even more difficult to write a parser that handles them in a general way, but I think you will agree that producing incorrect output without errors or warning is undesirable behavior.

TypeBox has Cyclic + TS parse / translation support built in these days.

Example Link Here

import Type from 'typebox'

type A = Type.Static<typeof A> // hover for inference
type B = Type.Static<typeof B> // hover for inference

// where A and B are TCyclic<{ .. }>
const { A, B } = Type.Script(`
export type A = {
    b: B[];
    c: {
        d: string;
    }
}

export type B = {
    a: A[];
}
`)

Unfortunately, I don't plan on making updates here as TypeBox has code translation built in (without the TS Compiler API as a dependency). I'm also not very interested in maintaining code translation for third party libraries anymore.

Would you like to take ownership of this project?

sinclairzx81 avatar Oct 30 '25 11:10 sinclairzx81

@sinclairzx81 thanks for the quick response! Wow, that's... very cool, I didn't know about this. So you wrote a TS parser both in code and type land? Insane, amazing job.

Do you think it would be worth the effort to replace TypeScriptToModel with Type.Script, and TypeScriptToTypeBox with Type.Script and a finished ModelToTypeBox? I see that ModelToTypeBox is TODO, but it shouldn't be too much work to write. It should then be possible to augment the other generators with Cyclic types in the longer run.

Anyway, thanks for the offer, I'm a bit unsure if I'm up to the task. Maybe in the meantime you could add me as a maintainer, and we'll see - I have my doubts, both knowledge/skill and free time-wise, but who knows. I already see an open Zod-related PR that's relevant to my work, so I can definitely count some work on this project toward my day job.

csorfab avatar Oct 30 '25 12:10 csorfab