Random game code id generator in typescript
One important consideration: should those who do not have the code for a room be able to guess it (or guess a random code) and enter a room they were never told the code for? If not, then 4 characters probably isn't quite enough:

If guesses and codes were random, the chance of guessing a lobby is on the order of magnitude of 100/450_000. Chances are low, but they're not astronomically low. To make it better, increase the number of characters in a code.
But long random character strings are difficult to remember. If you think that might be a problem for the UI, consider using random words instead - for example, have a dictionary of a large number of English words, and then to generate a code, put two or three of them together.


Is the method for generating codes efficient? Currently it just picks a random code and keeps incrementing until an unused code is found

If you really expect there to be less than 100 codes in use at a given time, then the chances of a collision are extremely low, so simply re-picking a random number in such rare cases sounds perfectly fine to me. If the app became more popular and collisions started happening on a regular basis, then it might be time to consider a different approach (such as hashing incremental numbers, or increasing the number of characters, or using a different algorithm entirely).

Is the method for encoding a number to an alphabet efficient? (converting a number to base 26)

When turning a number into a string, I'd prefer to use toString if possible. For example, if you're able to use numbers 0-9 in addition to letters, you could make encodeAlphabet much simpler and use:
  encodeAlphabet(num) {
    return num.toString(36);
  }

(though, for such an approach to be user-friendly, you might also want to remove all characters that might get confused with each other, like O and 0)
Another approach to consider - rather than generating a number and then turning it into a string, how about generating random characters and putting those into a string instead? No need to work with modulo and radix:
generateCode() {
  let code;
  do {
    code = this.getRandomCode();
  } while (this.usedCodes.has(code));
  this.usedCodes.add(code);
  return code;
}
getRandomCode() {
  return Array.from(
    { length: this.codeLength },
    () =&gt; String.fromCharCode(Math.floor(Math.random() * 26) + 65)
  ).join('');
}



const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

function randInt(min, max) {
  return Math.floor(Math.random() * (max - min)) + min;
}

class RoomCodeManager {
  // Generate 4-letter codes by default
  constructor(codeLength = 4) {
    this.codeLength = codeLength;
    this.usedCodes = new Set();
  }
  generateCode() {
    let code;
    do {
      code = this.getRandomCode();
    } while (this.usedCodes.has(code));
    this.usedCodes.add(code);
    return code;
  }
  getRandomCode() {
    return Array.from(
      { length: this.codeLength },
      () =&gt; String.fromCharCode(Math.floor(Math.random() * 26) + 65)
    ).join('');
  }
  releaseCode(code) {
    this.usedCodes.delete(code);
  }
}

const roomCodeManager = new RoomCodeManager();
console.log(roomCodeManager.generateCode());



Regarding
// If every code is already in use, just return a random code
if (this.usedCodes.size &gt;= max) {
  return this.encodeAlphabet(code);
}

Maybe, in such a case, increment this.codeLength instead of returning a duplicate? (Though, this'll probably never happen)