constructor(destinations = [], options = {}) {

    /**
     * An array of [`Output`](Output) objects to forward the message to.
     * @type {Output[]}
     */
    this.destinations = [];

    /**
     * An array of message types (`"noteon"`, `"controlchange"`, etc.) that must be matched in order
     * for messages to be forwarded. By default, this array includes all
     * [`Enumerations.SYSTEM_MESSAGES`](Enumerations#SYSTEM_MESSAGES) and
     * [`Enumerations.CHANNEL_MESSAGES`](Enumerations#CHANNEL_MESSAGES).
     * @type {string[]}
     */
    this.types = [
      ...Object.keys(Enumerations.SYSTEM_MESSAGES),
      ...Object.keys(Enumerations.CHANNEL_MESSAGES)
    ];

    /**
     * An array of MIDI channel numbers that the message must match in order to be forwarded. By
     * default, this array includes all MIDI channels (`1` to `16`).
     * @type {number[]}
     */
    this.channels = Enumerations.MIDI_CHANNEL_NUMBERS;

    /**
     * Indicates whether message forwarding is currently suspended or not in this forwarder.
     * @type {boolean}
     */
    this.suspended = false;

    // Make sure parameters are arrays
    if (!Array.isArray(destinations)) destinations = [destinations];
    if (options.types && !Array.isArray(options.types)) options.types = [options.types];
    if (options.channels && !Array.isArray(options.channels)) options.channels = [options.channels];

    if (WebMidi.validation) {

      // Validate destinations
      destinations.forEach(destination => {
        if ( !(destination instanceof Output) ) {
          throw new TypeError("Destinations must be of type 'Output'.");
        }
      });

      // Validate types
      if (options.types !== undefined) {

        options.types.forEach(type => {
          if (
            ! Enumerations.SYSTEM_MESSAGES.hasOwnProperty(type) &&
            ! Enumerations.CHANNEL_MESSAGES.hasOwnProperty(type)
          ) {
            throw new TypeError("Type must be a valid message type.");
          }
        });

      }

      // Validate channels
      if (options.channels !== undefined) {

        options.channels.forEach(channel => {
          if (! Enumerations.MIDI_CHANNEL_NUMBERS.includes(channel) ) {
            throw new TypeError("MIDI channel must be between 1 and 16.");
          }
        });

      }

    }

    this.destinations = destinations;
    if (options.types) this.types = options.types;
    if (options.channels) this.channels = options.channels;

  }
Creates a `Forwarder` object.

@param {Output|Output[]} [destinations=\[\]] An [`Output`](Output) object, or an array of such
objects, to forward the message to.

@param {object} [options={}]
@param {string|string[]} [options.types=(all messages)] A MIDI message type or an array of such
types (`"noteon"`, `"controlchange"`, etc.), that the specified message must match in order to
be forwarded. If this option is not specified, all types of messages will be forwarded. Valid
messages are the ones found in either
[`SYSTEM_MESSAGES`](Enumerations#SYSTEM_MESSAGES)
or [`CHANNEL_MESSAGES`](Enumerations#CHANNEL_MESSAGES).
@param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]]
A MIDI channel number or an array of channel numbers that the message must match in order to be
forwarded. By default all MIDI channels are included (`1` to `16`).