Can I extend the Client and the Room class?

Is the following (client-side) code possible?

import * as Colyseus from 'colyseus.js'

export class MyOwnClassExtendingColyseusRoom<T> extends Colyseus.Room<T> {

class MyOwnClassExtendingColyseusClient extends Colyseus.Client {

  protected createRoom<T> (roomName: string, rootSchema?: Colyseus.SchemaConstructor<T>) {
    return new MyOwnClassExtendingColyseusRoom(roomName, rootSchema) as Colyseus.Room<T>

I am using colyseus.js 0.14.1 and the compiler error message is:

Namespace '"c:/mygame/node_modules/colyseus.js/lib/index"' has no exported member 'SchemaConstructor'.

Bonus question: In case it is possible, is there any good reason against extending those classes?

Hi @TeeTeeHaa,

I'm curious why you'd prefer extending both the Client and the Room rather than dealing with their instances?

IMO if something useful on userland could be achieved only by extending the room that's something the implementation should provide to you instead of forcing you to extend it like this.

You could extend as you've mentioned, but as you're going to deal with internal methods, they can be changed on future releases

I want the code of my own classes for rooms look and work similar both on the client and on the server.

In a Colyseus server the function creating rooms, Server.define, takes a custom room class as argument. That custom class has to extend the server's room class, for example export class MyServerRoom extends Room<MyState>, and is instantiated by Server.define on demand.

export class MyServerRoom extends Room<MyState> { /* ... */ }

Server.define("myserverroom", MyServerRoom, options)

In a Colyseus client the functions creating rooms, Client.joinOrCreate or Client.consumeSeatReservation or the others, do not take a custom class as argument. Instead they instantiate and return (a promise to) the client's room class, Room<MyState>. As far as I know one cannot simply cast this to a custom room class which extends the client's room class.

Client.consumeSeatReservation<MyState>(roomReservation, MyState)
.then((room: Room<MyState>) => { /* ... */ }

The code in my first post tries to overload the client's internal method createRoom to instantiate a custom room class (extending the client's room class), which is then cast "down" to the client's room class and which is then cast "back up" to the custom room class after it is returned by Client.joinOrCreate or Client.consumeSeatReservation.

I assume what I want is Client.joinOrCreate or Client.consumeSeatReservation taking a custom room class (extending the client's room class) as argument and returning an instance of that class (instead of the plain client's room class).

Another thing about similarity: The server's room class has its "room lifecycle" methods, for example onCreate or onLeave, which can be overloaded in a custom class. The client's room class does not have those, but instead has member variables which can be set to callback functions, for example onStateChange or onLeave. That simply is not similar in my opinion - but probably it is like that for good reasons - reasons I do not understand yet.

Hey @TeeTeeHaa, the Room in the client-side is supposed to expose all its useful events as callbacks (onLeave, onStateChange, etc), I might be wrong but I can't see an explicit advantage for declaring your own room class as opposed to doing what's provided & documented, such as:

try {
  const room = await client.joinOrCreate<MyState>("room", {});

  console.log("client joined!");
  room.onLeave(() => console.log("client left!"));
  room.onStateChange(() => console.log("state changed!"));

  // (...schema callbacks)
  room.state.players.onAdd = (player, key) => {/* ... */};

} catch (e) {

You'd generally create your own wrapper for the room on the client-side, connecting its events with changes in your client's UI and representation of the game state.

If you have a better proposal for dealing with this on the client-side I'm all in, though. Just have to convince me and other team members :D