Socket.io Node.js Object Oriented Code Structure Approach following application:
Users can join Rooms. A Gameroom inherits from Room and has a queue where Users can queue for a game. If enough people queue they get thrown into a game lobby. Lobby also inherits from Room. Rooms implement chat functionality which should be used in Gamerooms and Lobbies aswell.

Right now I am passing the io Object to the instantiations and use it to listen to events specific to their class features.

What improvements can I do? Why is this a bad or good design choice? What alternatives are there? What is there to consider?
Should I split  the classes "Room", "Gameroom" and "Lobby" into a more distinct Model &amp; Controller?

UML Diagram

Server instantiates the Rooms.
Server.js

const io = socket.listen(
    http.createServer(app).listen(8080)
  );

console.log('Server listening..');

app.get('/', function (req, res) {
    res.sendFile(__dirname + '/test.html');
  });

var main = new Room('', io);
var a = new Gameroom('a', io);
var b = new Gameroom('b', io);


Room is the parent class, but can also be instantiated. It should be a root channel where everyone joins when they login.
Room.js

class Room {
  constructor(name, io) {
    this.name = name;
    this.users = [];
    this.namespace = io.of('/' + name);
    this.listenOnRoom();
  }

  listenOnRoom() {
    this.namespace.on('connection', (socket) =&gt; {
      ...

      socket.on('disconnect', (msg) =&gt; {
        ...
      });

      socket.on('chatMessage', (msg) =&gt; {
        ...
      });
    });
  }
}
module.exports = Room;


Gameroom adds the ability to queue for a game.
Gameroom.js

class Gameroom extends Room {
  constructor(name, io) {
    super(name, io);
    this.io = io;
    this.queue = [];
    this.lobbies = [];
    this.listenOnGameroom();
  }

  listenOnGameroom() {
    this.namespace.on('connection', (socket) =&gt; {

      socket.on('userJoinQueue', () =&gt; {
        ...
        this.lobbies.push(new Lobby(this.name ,lobbyId, participants, this.io));
      });

      socket.on('userLeaveQueue', () =&gt; {
        ...
      });

      socket.on('userLeaveLobby', () =&gt; {
        ...
      });
    });
  }
}

module.exports = Gameroom;


Lobby is instantiated by the Gameroom when there are enough players in the queue.
Lobby.js

class Lobby extends Room {
  constructor(gameroom, name, users, io) {
    super(gameroom + '/' + name, io);
    this.users = users;
    this.readyUsers = [];
    this.listenOnLobby();
  }

  listenOnLobby() {
    this.namespace.on('connection', (socket) =&gt; {

      socket.on('userReady', () =&gt; {
        ...
      });

      socket.on('userUnready', () =&gt; {
        ...
      });
    });
  }
}

module.exports = Lobby;
Just a few comments:

1:
The Room class can quickly become a place to throw in all sorts of functionality, even when it's not needed by all its children. 
I would be careful to not put much there at all, and rather compose the different rooms with needed functionality.
For example, a GameRoom may be composed by a chat, game queue, lobbies, and even user management.
Using composition like this can make it much easier to build different types of rooms, without having to throw every bit of common code into Room. I would even consider removing Room altogether if possible.

2:
In your socket callbacks, I would only put in single function calls to methods in your class.
This way, if you want to test one of these functions, you don't need to go via the socket callbacks.
Also, when instantiating your classes, I would consider not running the listener immediately.
If you're testing this, you don't want the hassle of mocking up io if you don't need to. 

But I think an even better way to do this would be to abstract away the socket stuff from these classes entirely.
This could be a bit too much though, depending on the complexity of the app.

3:
Consider replacing 

this.lobbies.push(new Lobby(this.name ,lobbyId, participants, this.io));


with something like

this.lobbies.push(this.lobbyFactory.create(this.name, lobbyId, participants));


This is useful for testing, since you don't force the usage of one particular lobby.
It's also useful if you decide to have different types of lobbies later on.
Then you can just inject another type of lobbyFactory.