-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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
[typescript][typescript-node] Serialize/deserialize maps and nullable types correctly #19362
[typescript][typescript-node] Serialize/deserialize maps and nullable types correctly #19362
Conversation
This fixes deserializing of models that are composed with null, undefined, or are inside of a map.
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.
thanks for your contribution! can you please update the code according to the comments (and apply the same changes also to the other parts of your PR)?
@@ -68,6 +68,16 @@ let typeMap: {[index: string]: any} = { | |||
{{/models}} | |||
} | |||
|
|||
// Check if a string starts with another string without using es6 features | |||
function startsWith(str: string, match: string): boolean { | |||
return str.lastIndexOf(match, 0) === 0; |
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.
why not indexOf?
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 was using the same logic that the existing code uses. But now that you mention it indexOf
would make more sense.
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.
That being said, is it really necessary to preserve pre-es6 compatibility here? startsWith
and endsWith
have been supported in Node.js and every major browser since 2015.
|
||
// Check if a string ends with another string without using es6 features | ||
function endsWith(str: string, match: string): boolean { | ||
return str.length > match.length && str.substring(str.length - match.length) === match; |
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.
return str.length > match.length && str.substring(str.length - match.length) === match; | |
return str.length >= match.length && str.substring(str.length - match.length) === match; |
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.
Good catch. It would be fine without it because all of the matches being passed in are strict subsets (there is guaranteed to be more string than match), but still good to be correct!
if (data === null) { | ||
return data | ||
} else { | ||
let subType: string = type.substring(0, type.length - 7); // Type | null => Type |
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.
let subType: string = type.substring(0, type.length - 7); // Type | null => Type | |
let subType: string = type.substring(0, type.length - " | null".length); // Type | null => Type |
and i would extract " | null"
to a separate constant
similar for the below undefined case
@@ -118,6 +142,17 @@ export class ObjectSerializer { | |||
transformedData.push(ObjectSerializer.serialize(datum, subType)); | |||
} | |||
return transformedData; | |||
} else if (startsWith(type, '{ [key: string]: ')) { | |||
let subType: string = type.replace('{ [key: string]: ', ''); // { [key: string]: Type; } => Type; } |
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 would suggest a regex here. can Type
be a map again, i.e. contain { [key: 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.
I would suggest a regex here.
Why a regex? I don't see how that would improve this code at all. Though now that I think about this more a .slice
would probably be more concise anyway.
can
Type
be a map again, i.e. contain{ [key: string]:
?
Yes, which is fine because this code is recursive.
@macjohnny I've updated the PR to use string constants instead of magic numbers. Please re-review when you have time. |
@simon-abbott can you please re-generate the samples? |
@macjohnny Samples regenerated. |
There was an edge case in the ObjectSerializer in both the
typescript
andtypescript-node
generators where schema objects (object types defined inschema.components
) were not properly deserialized if they were contained within a map (anobject
withadditionalProperties
) or a nullable array (anarray
where every entry is either a schema object ornull
). In these cases the ObjectSerializer would short-circuit and not actually deserialize the object. This results in the snake_cased keys in the API not being converted to the camelCase variants actually documented in the generated typescript types.I have included tests that cover this (and other) potential encoding and decoding edge cases. I'm sure I didn't cover every possible example, but I did confirm that as-written it will prevent a regression of this issue.
Note: I also removed the TS config setting
suppressImplicitAnyIndexErrors
from the templates. This was done becausesuppressImplicitAnyIndexErrors
is deprecated, and trying to build with it causes the typescript compiler to error, which was blocking thetypescript-node
tests from running.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
(upcoming 7.6.0 minor release - breaking changes with fallbacks),8.0.x
(breaking changes without fallbacks)Mentioning the TS committee members as per the checklist:
@TiFu @taxpon @sebastianhaas @kenisteward @Vrolijkx @macjohnny @topce @akehir @petejohansonxo @amakhrov @davidgamero @mkusaka