-
-
Notifications
You must be signed in to change notification settings - Fork 487
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
feat: Proposal of an approach to support readOnly / writeOnly properties #1863
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: Naoki Ikeguchi <me@s6n.jp>
|
Thanks so much for investigating this! 🙏 I think you’re on the right track in supporting this. I would honestly love for this to Just Work™ without a flag, and perhaps we can explore a solution that makes it invisible/backwards-compatible for users. If we find that, then we could just ship this. But yes, any breaking changes in behavior would have to be behind flags. That said, I’m pretty strongly against the current implementation detail of turning types into objects This isn’t a correct solution, but I’m interested if there are just additional types we can generate alongside the existing ones (playground): This approach:
I think probably a better solution overall is just generating an entirely-new Then in Generating additional types is not a breaking change. And the beauty of generating types is they don’t incur any runtime weight (yes, we will run into TS inference performance at scale, however, for this change just don’t even take that into consideration). For the purposes of this PR consider generating additional types (in the same file) “free.” Anyways, all that said, again, this is on the right path! But I would like to explore solutions that somehow sidestep touching the original If we can meet that goal, no flag needed 😎 |
Thank you for your feedback 🙏 I agree that the approach without any breaking changes nor flags is better at all. This was the biggest problem of my approach.
AFAIK we need to specify the generated paths interface to use it in import type {paths} from './$schema.js'
const client = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" }); So we cannot traverse 2 options here without changing the users' code like: import type {pathsWithVisibility} from './$schema.js';
const client = createClient<pathsWithVisibility>({ baseUrl: "https://myapi.dev/v1/" }); Also we need to produce 2x or more amount of the code to support the both of usages with or without visibility, for each components, paths, and operations 🤯. This affects all users regardless the user need the visibility or not. If we can switch the behaviour of the types generation, the size increase will not occur. Are there any solution to avoid breaking changes and flags without this problem? |
Yeah this is tough, and no straightfoward solution. I will say that don’t worry about the size/amount of types generated. Types are “free”—they all get baked out during the build and don’t impact runtime. That’s the whole philosophy of this library. This doesn’t directly answer your questions, but for the openapi-fetch and openapi-typescript library: Hard requirements
Note: some of the hard requirements here may make it seem like this feature is impossible to add. That’s not the case! But it is a strong indicator that this feature would warrant a breaking change for both. I was trying to see if there are creative solutions to adding this without a major version bump, but maybe not! And that’s OK. Soft goals (nice-to-haves)
Note that both of these things can be conceded as a last resort. As long as there’s justification for this being the only way. Again, this feature seems like it should be default behavior because it’s a part of the spec. And I’ll admit this part of the spec just wasn’t front-of-mind while developing 7.x and the initial version of openapi-fetch. This seems like it is deeper-reaching than originally-anticipated, which is also probably why it’s taken so long to add support. |
Also, just a thought 🤔 we may not need “2x” the generated types. Not if we generated the “raw” interfaces with the spread objects everywhere ( Now for openapi-fetch, this would still require that alternate Still, even if not, and openapi-fetch does require a separate generic out of necessity, I don’t mind that. We could make a semi-breaking change requiring this just for more correct OpenAPI behavior. This is basically your PR and approach 99% as you originally authored it, just with some very slight tweaks. |
Changes
refs #604
To support readOnly / writeOnly properties, I tried to generate a magic object type
{$read: T} | {$write: T}
and resolve the type withReadable<T>
andWritable<T>
helpers. This approach is inspired from ColumnType API in Kysely, a TypeScript-first query builder library.Since this pull request changes the generated type, it will be a breaking change for some users, I marked this as an experimental feature and require users to opt-in the feature if needed.
This pull request is working, but still just a PoC and some improvements may be needed. I would like to request you all for any ideas or concerns about this approach.
How to Review
experimentalVisibility
is turned offreadOnly
orwriteOnly
properties can be transformed into a magic{$read: T}
{$write: T}
type correctlyTS Playground for reviewing type utilities: https://www.typescriptlang.org/play/?#code/C4TwDgpgBAShCGATA8gOwDYgDwBUB8UAvFAN5QAkATgogFxQ5QC+A3AFBuiRQDqlAlsAhpMuAsTLkA7gKH1GrDl2hwkIkAGkI2fEVJQA2hqj9UUANbaA9gDMGAXXlH7UCAA8hqRAGdYNdVgArqjmqFZSqAQA-FDG9KgQAG4QlMwGliC2Duyc4NB8gsIYmtpiemRGJmYZWTiODM6uHhBevgVCAcGh4ZFQMXFQCcmpTOnWdnU5yn5I8ABG6BBlxMgAtoJYbFD6laYW4w5OGi7unj4zKMVYpjYpsNGwUEcnza1+AMZWlIhY3sACqAA5gAaKBdMIRB6qRDzRa4ZwEZ7MYFbXiyIqYLQ6PBsPBTPJowSwpa6FbrYCbbYkVHbXbVA51JGnFrndoY7A3O48B48J4NY5NM6+OCfb6-f6mEFgkIQ3oxdrE+HHRH8+zsbZMFHbaHqLFiXH47jkbzvAAWEFW8HKNJMdFIVBo9D+AMBim221Q8FWECdEqB6vdiAgJoEYGA-CsqCivpdAe2YHg3m8Ui+dpI0nRMclikUbAAFNT3YMvT6oAByGxWKxlrXuhNJlPfehl+CtmtsJhQbzwcPeGz8YOE4CK41mi3wPAAShyBZt-DtLbm73bRc93ublerKM73d7-cH0JHJvNlqn7CAA
Checklist
docs/
updated (if necessary)pnpm run update:examples
run (only applicable for openapi-typescript)