Source: Stack.js

const [Stack, TypedStack, SizeLimitedStack] = ( function(){
	/**
	 * A class that represents a stack. When an item is added, it goes to the top. When an item is removed, it is removed from the top.
	 * @class
	 */
	class Stack {
		/**
		 * @constructs Stack
		 */
		constructor() {
			this.__elems__ = [];
		}
		/**
		 * The number of items in the stack.
		 */
		get items() {
    	return this.__elems__.length;
		}
		/**
		 * Add an item to the stack.
		 * @param {...*} items The items to add.
		 * @returns {undefined}
		 */
		add( ...items ) {
			this.__elems__.push( ...items );
		}
		/**
		 * Remove an item from the stack.
		 * @returns {*} The item.
		 */
		remove() {
			return this.__elems__.pop();
		}
		*[Symbol.iterator]() {
			while( this.items ) {
    	  yield this.remove();
    	}
		}
	}
	/**
    * A stack class that only allows only specific types.
    * @class
    * @extends Stack
    */
	class TypedStack extends Stack {
		/**
   	 * @constructs TypedStack
   	 * @param {...Class} types The types to allow.
   	 * @throws {TypeError} You provided an invalid type!
   	 */
		constructor( ...types ) {
			if( types.all( ( type )=>typeof type === 'function' && type.prototype ) ) {
		      super();
  				this.__types__ = types;
    		} else {
    	  		throw TypeError( 'TypedStack expects an array of types.' );
    		}
		}
		/**
    	 * Add an item to the stack. Throws a `TypeError` if it is not an instance of any of the types.
       * @param {...*} items The items to add.
       * @throws {TypeError} Invalid item provided.
       * @returns {undefined}
       */
		add( ...items ) {
			items.forEach( ( item, _ ) => {
				if( this.__types__.some( type=>item instanceof type ) ) {
					super.add( item );
				} else {
					throw TypeError( `Invalid item: ${item}` );
				}
			} );
		}
	}
	/**
	 * A stack class with limited size.
	 * @class
	 * @extends Stack
	 */
	class SizeLimitedStack extends Stack {
		/**
		 * @constructs SizeLimitedStack
		 * @param {number} size The max on the number of items.
		 */
		constructor( size ) {
		  super();
		  this.__elems__ = Array( size ).fill();
		}
		/**
		 * Add an item to the stack. Note: Items that are pushed off the front are lost.
		 * @param {...*} items The items to add.
		 * @returns {undefined}
		 */
		add( ...items ) {
			items.forEach( ( item, _ ) => {
			  super.add( item );
			  this.__elems__.shift();
			} );
		}
	}
	/**
	 * A stack thats with limited size and specific types.
	 * @class
	 * @extends Stack
	 */
	class TypedSizeLimitedStack extends Stack {
		constructor( size, ...types ) {
		  if( types.all( ( type )=>typeof type === 'function' && type.prototype ) ) {
		      super();
  				this.__types__ = types;
  				this.__elems__ = Array( size ).fill();
    		} else {
    	  		throw TypeError( 'TypedStack expects an array of types.' );
    		}
		}
	}
	return [Stack, TypedStack, SizeLimitedStack];
} )();
/**
 * A constructor or class.
 * @alias Type
 * @typedef {function} Type
 * @memberof TypedStack
 */