Extending Yjs Types without breaking everything

Hi there I’m currently developing https://archibase.dev with Yjs to provide real-time database schema edition.

I came with the idea to create custom Yjs types to extends defaults shared types methods and to get a better type experience DX, code clarity etc.
For example


export class YColumn extends Map<any> { 
...

  setConstraints = (key:string, value:any) => {
    const type = this.get("constraints") as YMap<any>
    type.set(key, value)
  };

...
}

// this allows me to perform conveniently a 
column.setConstraints("default", true)
// whereever I have a reference to it. 

But when we encode state as update or play with the history, custom types are loss with their functionalities and the app crashes as expected.

Is this something possible, or should I move all the methods elsewhere and leave the Shared types alone?

1 Like

I tackled this via composition rather than inheritance, as I ran into similar roadblocks.

3 Likes

I’m would recommend you to check out runtime type checking libraries like zod and io-ts. These check that arbitrary JSON conforms to a defined type, and this defined type interoperates perfectly with typescript as well (so you don’t need to write your type definitions twice and risk inconsistencies.)

2 Likes

Thanks all, wiil do :slight_smile:

hey @braden, @ViktorQvarfordt, and @arnaudDerbey, could any of you share a short snippet on how you approached this issue?

I am in the same condition… if I persist the data (for example with IndexedDBProvider), when the data is loaded is received as the native Yjs class, and the application breaks everywhere :confused:

1 Like

I have a similar issue here

The way I have “solved” it temporarily is…

instead of using inheritance:

import * as Y from "yjs";

export class MyType extends Y.XmlElement {
   constructor() {
     super();
     //....
   }

   myMethod(parameter: parameterType): returnType {
     //...
   }
}

Extend the prototype:

import * as Y from "yjs";

export type MyType = Y.XmlElement;

declare module "yjs" {
   export interface XmlElement {
     myMethod(parameterType): returnType;
     // other methods or properties...
   }
}

Y.XmlElement.prototype.myMethod = function (parameterType) {
  // ...
};

But this is very awful… I would love to know how other people solved it :pray:

3 Likes

Any updates on this, did you try class composition ?