A queue is a first-in/first-out collection data structure with a time complexity of O(1) for key operations. We can easily model this in TypeScript using a generic class for items of type T.
A queue has two key operations -- enqueue, which adds an item to the queue in a time complexity of O(1), and then there is the dequeue operation, which removes the first item that was added to the queue, again in a time complexity of O(1).
If there are no more items in the queue, then it can return something that is out of bound, for example, undefined. Of course, this information can be encoded as a return value for the dequeue method of the queue class.
Not all is lost, though, because we can implement a queue by internally using a map. Here, we simply use a null prototype object as a map. With this map, we will need to track the valid range of indexes in the form of the last dequeued index, as well as the next enqueue index. Both of these, we initialize to zero.
Whenever we need to enqueue a new item, we will store the item at the current next enqueue index within the internal data map, followed by incrementing the next enqueue index for any future calls to enqueue.
Whenever there is a call to dequeue, we simply check if the last dequeued index hasn't caught up to the next enqueue index. If it has, then we let the default return of undefined handle that scenario.
Otherwise, we load the item that we plan to dequeue from the internal data map. We then delete it from the internal map to conserve space. Next, we increment our last dequeued index for any future calls to dequeue and finally return this dequeued item.
In short, the implementation is simply maintaining a map and tracking our queue and dequeue indexes within the enqueue and dequeue methods.
The common additional requirement of adding a size function, which returns a number of elements in the queue, can easily be met by diff-ing the next enqueue index with the last dequeued index.