index.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /**
  2. * @callback Handler
  3. * Handle a value, with a certain ID field set to a certain value.
  4. * The ID field is passed to `zwitch`, and it’s value is this function’s
  5. * place on the `handlers` record.
  6. * @param {...any} parameters
  7. * Arbitrary parameters passed to the zwitch.
  8. * The first will be an object with a certain ID field set to a certain value.
  9. * @returns {any}
  10. * Anything!
  11. */
  12. /**
  13. * @callback UnknownHandler
  14. * Handle values that do have a certain ID field, but it’s set to a value
  15. * that is not listed in the `handlers` record.
  16. * @param {unknown} value
  17. * An object with a certain ID field set to an unknown value.
  18. * @param {...any} rest
  19. * Arbitrary parameters passed to the zwitch.
  20. * @returns {any}
  21. * Anything!
  22. */
  23. /**
  24. * @callback InvalidHandler
  25. * Handle values that do not have a certain ID field.
  26. * @param {unknown} value
  27. * Any unknown value.
  28. * @param {...any} rest
  29. * Arbitrary parameters passed to the zwitch.
  30. * @returns {void|null|undefined|never}
  31. * This should crash or return nothing.
  32. */
  33. /**
  34. * @template {InvalidHandler} [Invalid=InvalidHandler]
  35. * @template {UnknownHandler} [Unknown=UnknownHandler]
  36. * @template {Record<string, Handler>} [Handlers=Record<string, Handler>]
  37. * @typedef Options
  38. * Configuration (required).
  39. * @property {Invalid} [invalid]
  40. * Handler to use for invalid values.
  41. * @property {Unknown} [unknown]
  42. * Handler to use for unknown values.
  43. * @property {Handlers} [handlers]
  44. * Handlers to use.
  45. */
  46. const own = {}.hasOwnProperty
  47. /**
  48. * Handle values based on a field.
  49. *
  50. * @template {InvalidHandler} [Invalid=InvalidHandler]
  51. * @template {UnknownHandler} [Unknown=UnknownHandler]
  52. * @template {Record<string, Handler>} [Handlers=Record<string, Handler>]
  53. * @param {string} key
  54. * Field to switch on.
  55. * @param {Options<Invalid, Unknown, Handlers>} [options]
  56. * Configuration (required).
  57. * @returns {{unknown: Unknown, invalid: Invalid, handlers: Handlers, (...parameters: Parameters<Handlers[keyof Handlers]>): ReturnType<Handlers[keyof Handlers]>, (...parameters: Parameters<Unknown>): ReturnType<Unknown>}}
  58. */
  59. export function zwitch(key, options) {
  60. const settings = options || {}
  61. /**
  62. * Handle one value.
  63. *
  64. * Based on the bound `key`, a respective handler will be called.
  65. * If `value` is not an object, or doesn’t have a `key` property, the special
  66. * “invalid” handler will be called.
  67. * If `value` has an unknown `key`, the special “unknown” handler will be
  68. * called.
  69. *
  70. * All arguments, and the context object, are passed through to the handler,
  71. * and it’s result is returned.
  72. *
  73. * @this {unknown}
  74. * Any context object.
  75. * @param {unknown} [value]
  76. * Any value.
  77. * @param {...unknown} parameters
  78. * Arbitrary parameters passed to the zwitch.
  79. * @property {Handler} invalid
  80. * Handle for values that do not have a certain ID field.
  81. * @property {Handler} unknown
  82. * Handle values that do have a certain ID field, but it’s set to a value
  83. * that is not listed in the `handlers` record.
  84. * @property {Handlers} handlers
  85. * Record of handlers.
  86. * @returns {unknown}
  87. * Anything.
  88. */
  89. function one(value, ...parameters) {
  90. /** @type {Handler|undefined} */
  91. let fn = one.invalid
  92. const handlers = one.handlers
  93. if (value && own.call(value, key)) {
  94. // @ts-expect-error Indexable.
  95. const id = String(value[key])
  96. // @ts-expect-error Indexable.
  97. fn = own.call(handlers, id) ? handlers[id] : one.unknown
  98. }
  99. if (fn) {
  100. return fn.call(this, value, ...parameters)
  101. }
  102. }
  103. one.handlers = settings.handlers || {}
  104. one.invalid = settings.invalid
  105. one.unknown = settings.unknown
  106. // @ts-expect-error: matches!
  107. return one
  108. }