-
Notifications
You must be signed in to change notification settings - Fork 70
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
Fix: maps, sets and vector types #203
Conversation
src/collections/lookup-map.ts
Outdated
@@ -13,7 +13,7 @@ export class LookupMap { | |||
return near.storageHasKey(storageKey) | |||
} | |||
|
|||
get(key: Bytes): unknown | null { | |||
get(key: Bytes): T | null { |
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.
The issue is what this get is an object instead of T
. For example, the CarSpec get will be {id, color, price, engine}
, not CarSpec{id, color, price, engine}
src/collections/unordered-map.ts
Outdated
let indexRaw = getIndexRaw(this.keyIndexPrefix, key); | ||
if (indexRaw) { | ||
let index = deserializeIndex(indexRaw); | ||
let value = this.values.get(index); | ||
if (value) { | ||
return value; | ||
return value as T; |
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.
as T
also doesn't solve the problem, it's only a ts sytax suger. When inspect the built JS contract, it is ignored. For example: https://www.typescriptlang.org/play?#code/LAKFGMBsEMGdYAQGFoCcDKAHApuBBvUBYhcAewDtYAXVAV3GrNQApM6AjSASzwugC22AFwIaqbhQDmASgIBfUIrAhI2aggAeCALwF+Q0QHJoR5Wo0BPXVoRxkaLLgDcESrDJqAdJDJSW1pI00BTg2GQAZg4YOOAyQA
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.
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.
Looks like this feature is not related to deserialization. Can you please elaborate on how it can help?
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.
When constructing a collection we could set the private field (brand) to the type of the underlying data we store, this would be serialized and stored which means we could theoretically infer the type with this field when deserializing the collection. We could also just let the user decide what the type is which is easier, and I don't know how valuable inferring the type is anyway. That's why I am saying it doesn't really matter that it says as T
since types in JS/TS are just for developer experience anyway
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'm not sure that this solution here is 100% okay, but something in this direction could go:
I don't know if it's okay to ask the user to pass us an instance of the class to constructor :D
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.
@petarvujovic98 what are you think about this example?
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.
@osalkanovic I wouldn't use this approach as the instance is going to also get serialized so you will lose the constructor, plus if it is a primitive type it won't need to have a constructor and you are complicating the DX.
Keeping track of type data after seralization is difficult in JS, but since users are using TS to write the construction and access of variables after deserialization, it's better to keep the logic for those in the TS layer rather than in the JS/runtime layers.
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 agree with @petarvujovic98 , and the way @osalkanovic shown is quite clever that I didn't think of. May @osalkanovic 's way be incorporated in as a pattern to write deserializer?: (value: unknown) => T
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.
@ailisp I will update PR with deserializer today.
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.
@ailisp @volovyks @petarvujovic98 PR is ready for review
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 the PR! There's still some problems on recovering the type of the object, so still cannot consider it as T
🤔
src/collections/unordered-map.ts
Outdated
@@ -59,21 +59,21 @@ export class UnorderedMap { | |||
return keysIsEmpty; | |||
} | |||
|
|||
get(key: Bytes): unknown | null { | |||
get(key: Bytes, deserializer?: (value: unknown) => DataType): DataType | null { |
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.
@petarvujovic98 since I see that you are working on the default value, maybe it would be good to convert this to options
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.
@osalkanovic Seems reasonable to me. This also allows for more extensibility in the future
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.
Overall your work looks great! Thanks!
I'd like to add a small naming comment: in near-bindgen.ts, we call the process from object to DataType as "reconstruct", and deserializer in libraries usually mean from string/bytes to object. So I wonder if we should name it as reconstructor
instead of deserializer
?
The other way is make deserializer calls on raw
instead of on value
, this allow user to even override the JSON.parse
with a customizable format, but this seems to be more scoped than this PR intended and a serializer?
argument is better to come together.
Therefore I suggest naming it as reconstructor
, and make a room for future adding deserializer
the get by changing the signatures from:
get(key: Bytes, reconstructor?: (value: unknown) => DataType): DataType | null {
to
get(key: Bytes, options?: {reconstructor?: (value: unknown) => DataType}): DataType | null {
And, could you please change a test in tests/src/unordered-map.ts to ensure get with option works. We have an existing test that does manually reconstruct:
near-sdk-js/tests/src/unordered-map.js
Line 61 in ef90663
get_house() { |
With your PR a house will be get directly!
@ailisp Updated with latest develop, ready for merge! :) |
@osalkanovic Fantastic! You have even implemented generics for the new lookup map. I think this PR is in production-ready quality. @volovyks If this also looks good to you, let's wait NEARCON end (at Sep 14 and merge this!) |
@osalkanovic We turned on ci, if you could resolve and merge latest develop into PR. If everything is good on CI we can merge it! |
I will update today |
@ailisp Ready for merge |
@osalkanovic Thank you! |
Issue: #199