/** * @callback Handler * Handle a value, with a certain ID field set to a certain value. * The ID field is passed to `zwitch`, and it’s value is this function’s * place on the `handlers` record. * @param {...any} parameters * Arbitrary parameters passed to the zwitch. * The first will be an object with a certain ID field set to a certain value. * @returns {any} * Anything! */ /** * @callback UnknownHandler * Handle values that do have a certain ID field, but it’s set to a value * that is not listed in the `handlers` record. * @param {unknown} value * An object with a certain ID field set to an unknown value. * @param {...any} rest * Arbitrary parameters passed to the zwitch. * @returns {any} * Anything! */ /** * @callback InvalidHandler * Handle values that do not have a certain ID field. * @param {unknown} value * Any unknown value. * @param {...any} rest * Arbitrary parameters passed to the zwitch. * @returns {void|null|undefined|never} * This should crash or return nothing. */ /** * @template {InvalidHandler} [Invalid=InvalidHandler] * @template {UnknownHandler} [Unknown=UnknownHandler] * @template {Record} [Handlers=Record] * @typedef Options * Configuration (required). * @property {Invalid} [invalid] * Handler to use for invalid values. * @property {Unknown} [unknown] * Handler to use for unknown values. * @property {Handlers} [handlers] * Handlers to use. */ const own = {}.hasOwnProperty /** * Handle values based on a field. * * @template {InvalidHandler} [Invalid=InvalidHandler] * @template {UnknownHandler} [Unknown=UnknownHandler] * @template {Record} [Handlers=Record] * @param {string} key * Field to switch on. * @param {Options} [options] * Configuration (required). * @returns {{unknown: Unknown, invalid: Invalid, handlers: Handlers, (...parameters: Parameters): ReturnType, (...parameters: Parameters): ReturnType}} */ export function zwitch(key, options) { const settings = options || {} /** * Handle one value. * * Based on the bound `key`, a respective handler will be called. * If `value` is not an object, or doesn’t have a `key` property, the special * “invalid” handler will be called. * If `value` has an unknown `key`, the special “unknown” handler will be * called. * * All arguments, and the context object, are passed through to the handler, * and it’s result is returned. * * @this {unknown} * Any context object. * @param {unknown} [value] * Any value. * @param {...unknown} parameters * Arbitrary parameters passed to the zwitch. * @property {Handler} invalid * Handle for values that do not have a certain ID field. * @property {Handler} unknown * Handle values that do have a certain ID field, but it’s set to a value * that is not listed in the `handlers` record. * @property {Handlers} handlers * Record of handlers. * @returns {unknown} * Anything. */ function one(value, ...parameters) { /** @type {Handler|undefined} */ let fn = one.invalid const handlers = one.handlers if (value && own.call(value, key)) { // @ts-expect-error Indexable. const id = String(value[key]) // @ts-expect-error Indexable. fn = own.call(handlers, id) ? handlers[id] : one.unknown } if (fn) { return fn.call(this, value, ...parameters) } } one.handlers = settings.handlers || {} one.invalid = settings.invalid one.unknown = settings.unknown // @ts-expect-error: matches! return one }