get(name, TypeConstructor = AbstractType) {
    const type = map.setIfUndefined(this.share, name, () => {
      // @ts-ignore
      const t = new TypeConstructor();

      t._integrate(this, null);

      return t;
    });
    const Constr = type.constructor;

    if (TypeConstructor !== AbstractType && Constr !== TypeConstructor) {
      if (Constr === AbstractType) {
        // @ts-ignore
        const t = new TypeConstructor();
        t._map = type._map;

        type._map.forEach(
        /** @param {Item?} n */
        n => {
          for (; n !== null; n = n.left) {
            // @ts-ignore
            n.parent = t;
          }
        });

        t._start = type._start;

        for (let n = t._start; n !== null; n = n.right) {
          n.parent = t;
        }

        t._length = type._length;
        this.share.set(name, t);

        t._integrate(this, null);

        return t;
      } else {
        throw new Error(`Type with the name ${name} has already been defined with a different constructor`);
      }
    }

    return type;
  }
Define a shared data type.

Multiple calls of `y.get(name, TypeConstructor)` yield the same result
and do not overwrite each other. I.e.
`y.define(name, Y.Array) === y.define(name, Y.Array)`

After this method is called, the type is also available on `y.share.get(name)`.

*Best Practices:*
Define all types right after the Yjs instance is created and store them in a separate object.
Also use the typed methods `getText(name)`, `getArray(name)`, ..

@example
  const y = new Y(..)
  const appState = {
    document: y.getText('document')
    comments: y.getArray('comments')
  }

@param {string} name
@param {Function} TypeConstructor The constructor of the type definition. E.g. Y.Text, Y.Array, Y.Map, ...
@return {AbstractType<any>} The created type. Constructed with TypeConstructor

@public