Skip to content
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(toJSON): get the generic type T for toJSON response #6789

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/realm/src/JSONCacheMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
import { DefaultObject, INTERNAL, RealmObject } from "./internal";

/** @internal */
export class JSONCacheMap extends Map<number, Map<string, DefaultObject>> {
add<T>(object: RealmObject<T>, value: DefaultObject) {
export class JSONCacheMap<T = DefaultObject> extends Map<number, Map<string, T>> {
add<U>(object: RealmObject<U>, value: T) {
const tableKey = object[INTERNAL].table.key;
let cachedMap = this.get(tableKey);
if (!cachedMap) {
Expand All @@ -29,7 +29,7 @@ export class JSONCacheMap extends Map<number, Map<string, DefaultObject>> {
}
cachedMap.set(object._objectKey(), value);
}
find<T>(object: RealmObject<T>) {
find<U>(object: RealmObject<U>) {
return this.get(object[INTERNAL].table.key)?.get(object._objectKey());
}
}
6 changes: 3 additions & 3 deletions packages/realm/src/Object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,17 +382,17 @@ export class RealmObject<T = DefaultObject, RequiredProperties extends keyof Omi
* and [flatted](https://www.npmjs.com/package/flatted) to stringify Realm entities that have circular structures.
* @returns A plain object.
*/
toJSON(_?: string, cache?: unknown): DefaultObject;
toJSON(_?: string, cache?: unknown): T | Partial<T>;
Copy link
Member

@kraenhansen kraenhansen Jul 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the Partial<T>?

Also - let's assume the model class has a property of type Realm.List. This is expected to return an array of values through toJSON, in which case using the T directly would be incorrect. We need a mapping, similar to the one we do in src/Unmanaged.ts (used by the Realm#create method).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, we need to mapping the array case. I'll check how can I improve it.

About the partial, when we are creating and mapping the values to create the object we have a partial of T, and we save in the cache, if some we have some break in the flow the cache will still be there (I assume) and the fn will return the partial of T, this is the mutabillity problem. Should I consider that we will never break and always return a complete object?

Copy link
Member

@kraenhansen kraenhansen Jul 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we will never break and always return a complete object?

I not 100% sure I follow - please help me understand if I don't. What I think you're referring to is the fact that the object graph might contain cycles.

As I understand the toJSON semantics, it needs to transform objects into JSON serializable values, which might contain cycles and that's exactly what the implementation tries to do. I.e. if two objects refer to each other the cache would be used to detect the cycle and the resulting object would be two plain objects cyclicly referring to each other (as expected).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, on second thought, maybe we shouldn't lead the return as Partial<T>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think should return OmittedRealmTypes<T>

In document https://www.mongodb.com/docs/atlas/device-sdks/sdk/react-native/model-data/define-a-realm-object-model/

class Book extends Realm.Object<Book> {
  name!: string;
  price?: number;

  static schema: ObjectSchema = {
    name: 'Book',
    properties: {
      name: {type: 'string', indexed: true},
      price: 'int?',
    },
  };
}

T is Book (extends Realm.Object) so T has Realm.Object's methods, properties

but toJSON() remove those methods, properties like OmittedRealmTypes<T>

I patched it locally to toJSON(_?: string, cache?: unknown): OmittedRealmTypes<T>; and didn't encounter any issues while using it.

/** @internal */
toJSON(_?: string, cache = new JSONCacheMap()): DefaultObject {
toJSON(_?: string, cache = new JSONCacheMap<Partial<T>>()): T | Partial<T> {
// Construct a reference-id of table-name & primaryKey if it exists, or fall back to objectId.

// Check if current objectId has already processed, to keep object references the same.
const existing = cache.find(this);
if (existing) {
return existing;
}
const result: DefaultObject = {};
const result: Partial<T> = {};
cache.add(this, result);
// Move all enumerable keys to result, triggering any specific toJSON implementation in the process.
for (const key in this) {
Expand Down