realm / realm-js

Realm is a mobile database: an alternative to SQLite & key-value stores
https://realm.io
Apache License 2.0
5.6k stars 558 forks source link

Cannot index a property using TypeScript #6607

Open thegreatzeus opened 2 weeks ago

thegreatzeus commented 2 weeks ago

How frequently does the bug occur?

Always

Description

I'm trying to index a string property on a Realm model using TypeScript.

I'm using Realm's React Native SDK with Realm's Expo example app.

Using the same example from Realm's docs:

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

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

Results in the following error:

Classes extending Realm.Object cannot define their own `schema` static, 
all properties must be defined using TypeScript syntax

Stacktrace & log output

at visitRealmClass (/AtlasDeviceSdkApp/node_modules/@realm/babel-plugin/dist/plugin/index.js:323:15)
    at PluginPass.ClassDeclaration (/AtlasDeviceSdkApp/node_modules/@realm/babel-plugin/dist/plugin/index.js:375:25)
    at newFn (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/visitors.js:160:14)
    at NodePath._call (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/path/context.js:46:20)
    at NodePath.call (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/path/context.js:36:17)
    at NodePath.visit (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/path/context.js:82:31)
    at TraversalContext.visitQueue (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/context.js:89:16)
    at TraversalContext.visitSingle (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/context.js:65:19)
    at TraversalContext.visit (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/context.js:112:19)
    at traverseNode (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/traverse-node.js:22:17)
    at NodePath.visit (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/path/context.js:88:52)
    at TraversalContext.visitQueue (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/context.js:89:16)
    at TraversalContext.visitMultiple (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/context.js:61:17)
    at TraversalContext.visit (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/context.js:110:19)
    at traverseNode (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/traverse-node.js:22:17)
    at NodePath.visit (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/path/context.js:88:52)
    at TraversalContext.visitQueue (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/context.js:89:16)
    at TraversalContext.visitSingle (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/context.js:65:19)
    at TraversalContext.visit (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/context.js:112:19)
    at traverseNode (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/traverse-node.js:22:17)
    at traverse (/AtlasDeviceSdkApp/node_modules/@babel/traverse/lib/index.js:52:34)
    at transformFile (/AtlasDeviceSdkApp/node_modules/@babel/core/lib/transformation/index.js:82:31)
    at transformFile.next (<anonymous>)
    at run (/AtlasDeviceSdkApp/node_modules/@babel/core/lib/transformation/index.js:24:12)
    at run.next (<anonymous>)
    at /AtlasDeviceSdkApp/node_modules/@babel/core/lib/transform-ast.js:23:33
    at Generator.next (<anonymous>)
    at evaluateSync (/AtlasDeviceSdkApp/node_modules/gensync/index.js:251:28)
    at sync (/AtlasDeviceSdkApp/node_modules/gensync/index.js:89:14)
    at stopHiding - secret - don't use this - v1 (/AtlasDeviceSdkApp/node_modules/@babel/core/lib/errors/rewrite-stack-trace.js:47:12)
    at transformFromAstSync (/AtlasDeviceSdkApp/node_modules/@babel/core/lib/transform-ast.js:43:83)
    at Object.transform (/AtlasDeviceSdkApp/node_modules/metro-react-native-babel-transformer/src/index.js:201:20)
    at transformJSWithBabel (/AtlasDeviceSdkApp/node_modules/metro-transform-worker/src/index.js:330:45)
    at Object.transform (/AtlasDeviceSdkApp/node_modules/metro-transform-worker/src/index.js:461:18)
    at transformFile (/AtlasDeviceSdkApp/node_modules/metro/src/DeltaBundler/Worker.flow.js:73:36)
    at Object.transform (/AtlasDeviceSdkApp/node_modules/metro/src/DeltaBundler/Worker.flow.js:48:10)
    at execFunction (/AtlasDeviceSdkApp/node_modules/jest-worker/build/workers/processChild.js:137:17)
    at execHelper (/AtlasDeviceSdkApp/node_modules/jest-worker/build/workers/processChild.js:116:5)
    at execMethod (/AtlasDeviceSdkApp/node_modules/jest-worker/build/workers/processChild.js:120:5)
    at process.messageListener (/AtlasDeviceSdkApp/node_modules/jest-worker/build/workers/processChild.js:38:7)
    at process.emit (node:events:517:28)
    at emit (node:internal/child_process:944:14)

### Can you reproduce the bug?

Always

### Reproduction Steps

Use the Expo template app and try to define a new model using TypeScript such as [the example](https://www.mongodb.com/docs/realm/sdk/react-native/model-data/define-a-realm-object-model/#index-a-property):

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

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

Version

"realm": "12.0.0", "@realm/react": "^0.6.0",

What services are you using?

Local Database only

Are you using encryption?

No

Platform OS and version(s)

Android 14

Build environment

Node v18.19.1

package.json contents:

{
  "name": "atlasdevicesdkapp",
  "version": "1.0.0",
  "scripts": {
    "start": "expo start --dev-client",
    "android": "expo run:android",
    "ios": "expo run:ios"
  },
  "dependencies": {
    "@realm/react": "^0.6.0",
    "expo": "^49.0.8",
    "expo-dev-client": "~2.4.8",
    "expo-splash-screen": "~0.20.5",
    "expo-status-bar": "~1.6.0",
    "react": "18.2.0",
    "react-native": "0.72.4",
    "react-native-get-random-values": "~1.9.0",
    "realm": "12.0.0"
  },
  "devDependencies": {
    "@babel/core": "^7.22.5",
    "@babel/plugin-proposal-decorators": "^7.22.5",
    "@realm/babel-plugin": "^0.1.1",
    "@types/react": "~18.2.13",
    "typescript": "^5.1.3"
  },
  "license": "Apache-2.0",
  "private": true
}

Cocoapods version

No response

sync-by-unito[bot] commented 2 weeks ago

➤ PM Bot commented:

Jira ticket: RJS-2799

kneth commented 2 weeks ago

@thegreatzeus Thank you for reporting. We need to investigate.

favourwright commented 2 weeks ago

I've literally been having same issue! well, same error message Classes extending Realm.Object cannot define their ownschemastatic, all properties must be defined using TypeScript syntax

in my case I'm just trying to create a basic model like so

class Book extends Realm.Object<Book> {
  name!: string;
  price?: number;
  static schema: ObjectSchema = {
    name: 'Book',
    properties: {
      name: 'string',
      price: 'int?',
    },
  };
}
favourwright commented 2 weeks ago

@thegreatzeus any leads yet?

jpbast commented 2 weeks ago

Same issue here. Using @thegreatzeus class as example, if I just define my scheme as the following, it works fine

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

If I add the static scheme: ObjectSchema { ... } code it always throw the error mentioned here. It seems the only way to set a primaryKey is through this ObjectSchema but with this error going on it's impossible.

Is there any work around so far?

favourwright commented 2 weeks ago
import { Realm } from '@realm/react'
import { BeneficiaryType } from '...'

export class Beneficiary extends Realm.Object {
  _id!: Realm.BSON.ObjectId;
  name!: string;
  ...

  static generate(payload: BeneficiaryType) {
    return {
      _id: new Realm.BSON.ObjectId(),
      name: payload.name,
      ....
    };
  }
}

Object.defineProperty(Beneficiary, 'schema', {
  value: {
    name: 'Beneficiary',
    primaryKey: '_id',
    properties: {
      _id: 'objectId',
      name: 'string',
      ...
    },
  },
});

this is what I ended up using @jpbast

jpbast commented 2 weeks ago

@favourwright it worked for me! Thanks for your quick reply

elle-j commented 1 week ago

@thegreatzeus, it looks like you're using the @realm/babel-plugin. The idea of that plugin is to simplify some of the model/schema creation by removing the need for the explicit static schema field.

You can either remove the plugin and use the schema as you have defined it, or use the plugin and remove the static schema.

To index a property using the babel-plugin, use the @index annotation. To declare a primary key, use static primaryKey = _id (or whichever key is the pk).

The following is a similar example as the @realm/babel-plugin README:

class Task extends Realm.Object<Task, "description"> {
  _id = new Realm.BSON.ObjectId();
  description!: string;
  @index
  isComplete = false;

  static primaryKey = "_id";
}