-
-
Notifications
You must be signed in to change notification settings - Fork 6.7k
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
[bugfix] [typescript-fetch] add oneOf string union & array support (#19909) #20193
base: master
Are you sure you want to change the base?
[bugfix] [typescript-fetch] add oneOf string union & array support (#19909) #20193
Conversation
private static String getEnumStringLiteralUnionType(Schema interfaceSchema) { | ||
return (String) interfaceSchema.getEnum().stream() | ||
.map(enumName -> "\"" + (String)enumName + "\"") | ||
.collect(Collectors.joining(" | ")); |
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.
is that the same as https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractTypeScriptClientCodegen.java#L1123 ?
if overriding toOneOfName
won't meet your requirement, can you please elaborate?
cc @OpenAPITools/generator-core-team
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.
@wing328 Yea, they are pretty similar. The core difference is that toOneOfName
seems to build the whole oneOf type, while mine only builds the string literal part. It would be nice to refactor them both into one method.
The Problem is, toOneOfName
seems to be completely unused in the oneOf model generation.
Currently, the different oneOf union parts are just put in an array and are appended together inside the mustache, not using toOneOfName
at all.
Refactoring it to build the full oneOf type inside the toOneOfName
instead of inside the mustache seems to be a major change to how the code generation works and i need some input if this is the correct way, or if i misunderstood something, as i am new to the codebase
/** | ||
*/ | ||
async testArrayRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<TestArrayResponse>> { | ||
const queryParameters: any = {}; |
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.
can this be at least:
const queryParameters: any = {}; | |
const queryParameters: Record<string, any> = {}; |
?
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.
Yes, in fact, it can even be runtime.HTTPQuery
But i think this is outside of the scope of this PR as I did not make any changes to the api mustache, i just added a new testcase.
This PR is only about fixing the model mustache, not improving the api mustache, so I would recommend opening a new PR for this
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.
ah, sorry, I assumed because it was a newly generated block, it originated from your change, too.
if (!(json instanceof Object)){ | ||
return {} as any; | ||
} | ||
if (Array.isArray(json) && json.every(item => item instanceof String)) { |
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.
if these were type guards, Typescript can infer without the cast.
In this case for example:
function isStringArray(x: any): x is Array<string> {
return Array.isArray(x) && x.every(item => item instanceof String);
}
see playground
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.
Yes great idea, i'll implement it
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.
item instanceof String
is incorrect. you need typeof item === 'string'
instead.
you can check it in the console:
> "" instanceof String
< false
> typeof "" === 'string'
< true
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.
and also modern TS (5.5 or higher) is smart enough to infer type guards, you don't need a custom type guard here.
here is a modified playground: https://www.typescriptlang.org/play/?moduleResolution=99&target=7&module=6&resolvePackageJsonImports=true&allowArbitraryExtensions=false#code/MYewdgzgLgBAHjAvDA2gIgIZoDRoEZoC6MGEJYAngNwBQNAlgGYAUAggE7sYUB09EHLhWZwAlDABkE+DwCmAN1nth9KLIC2SAHwwoFAA6yQjGKo1JEyAOTR29MAHMro8QG8aMT-A9eA9L5gAPQB+GgBfIA
return {} as any; | ||
} | ||
|
||
export function TestArrayResponseToJSON(json: any): any { |
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.
can the return type be more narrow here?
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.
I think the best we can do is runtime.Json but this is also just an alias for any
so not really helpful.
Also I was also wondering why we have two methods: one typed named ToJSONTyped
and one untyped named ToJSON
. Do you think we can remove the untyped ToJSON?
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.
I am not sure why, maybe a backwards compat thing? We can alias one to the other. Anything but unknown
and never
is a subsitute for any
, so as long as we don't produce that we can remove the untyped variant implementation?
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.
^ @macjohnny probably knows more?
return TestArrayResponseToJSONTyped(json, false); | ||
} | ||
|
||
export function TestArrayResponseToJSONTyped(value?: TestArrayResponse | null, ignoreDiscriminator: boolean = false): any { |
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.
^ ditto?
} | ||
if (!(value instanceof Object)){ | ||
return {}; | ||
} | ||
if (instanceOfTestA(value)) { |
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.
Independent from your changes:
- mental note to look at the generated
instanceOf...
functions to make them type guards, so we can lose some casts.
if (Array.isArray(json) && json.every(item => item instanceof String)) { | ||
return json as Array<string>; | ||
} | ||
if (Array.isArray(json) && json.every(item => item instanceof Object)) { |
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.
same here: instanceof Object
is probably not what you need.
e.g. [] instanceof Object // true
use typeof
instead
return json as Array<string>; | ||
} | ||
if (Array.isArray(json) && json.every(item => item instanceof Object)) { | ||
if (json.every(item => instanceOfTestA(item as Object))) { |
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.
once you have changed instanceof Object
to typeof x === 'object
, you will not need this type assertion (as Object
)
Fixes #19909
PR checklist
Commit all changed files.
This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
These must match the expectations made by your contribution.
You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example
./bin/generate-samples.sh bin/configs/java*
.IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
master
(upcoming7.x.0
minor release - breaking changes with fallbacks),8.0.x
(breaking changes without fallbacks)@TiFu (2017/07) @taxpon (2017/07) @sebastianhaas (2017/07) @kenisteward (2017/07) @Vrolijkx (2017/09) @macjohnny (2018/01) @topce (2018/10) @akehir (2019/07) @petejohansonxo (2019/11) @amakhrov (2020/02) @davidgamero (2022/03) @mkusaka (2022/04) @joscha (2024/10)