All files / src/function utility.js

100% Statements 136/136
100% Branches 9/9
100% Functions 5/5
100% Lines 136/136

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 1461x   1x 1x   1x   1x   1x 1x 1x 1x 1x 1x 1x 1x 1x 202x 202x   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 95x 95x   1x   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 125x 125x 125x 125x 125x 125x 45x 45x 125x 125x   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 93x 93x 93x   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
// @ts-check
 
import { getOwnPropertyDescriptor } from '../utility';
import { isFunction } from '../base';
 
// import { isAnyGeneratorFunction } from './index';
 
// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
 
/**
 * Detects whether a passed value features an own `prototype` property.
 * @param {Function} value
 *  Assumes a function type but does not check for it.
 * @returns {boolean}
 *  Whether the passed type features an own `prototype` property.
 * @category Function Type Detection Helper
 */
export function hasOwnPrototype(value) {
  return !!getOwnPropertyDescriptor(value, 'prototype');
}
 
/**
 * Detects whether a passed value features an own, truly `writable`
 * `prototype` property.
 * @param {Function} value
 *  Assumes a function type but does not check for it.
 * @returns {boolean}
 *  Whether the passed type features an own, truly `writable`
 *  `prototype` property.
 * @category Function Type Detection Helper
 */
export function hasOwnWritablePrototype(value) {
  return getOwnPropertyDescriptor(value, 'prototype')?.writable === true;
}
 
// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
 
/**
 * Checks whether a passed `value` is possibly constructable.
 *
 * It does so by just probing the passed value's `[[construct]]`
 * slot; it does never invoke the passed value itself.
 *
 * The `construct` proxy handler is allowed to overwrite the
 * `[[construct]]` slot of a proxied value, but it cannot turn
 * something non constructable into a constructable type.
 *
 * - see ... [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/construct]
 * > "The `handler.construct` method is a trap for the `[[Construct]]`
 * > object internal method, which is used by operations such as the
 * > `new` operator. In order for the `new` operation to be valid on
 * > the resulting `Proxy` object, the `target` used to initialize
 * > the proxy must itself be a valid constructor."
 * @param {any} value
 * @returns {boolean}
 *  A boolean value which indicates whether the tested
 *  type could possibly serve as constructor function.
 * @category Function Type Detection Helper
 */
export function hasConstructSlot(value) {
  let canConstruct = false;
  try {
    // eslint-disable-next-line no-new
    new new Proxy(value, { construct: () => ({}) })();
    canConstruct = true;
  } catch (err) {
    /* empty */
  }
  return canConstruct;
}
 
/**
 * Detects whether the passed `value` is a constructable function type.
 *
 * It does so by verifying whether a function has an own `prototype`, and in
 * case it does, by additionally just probing the `[[construct]]` slot of the
 * passed possibly constructable type; it does never invoke the passed type.
 *
 * The `construct` proxy handler is allowed to overwrite the
 * `[[construct]]` slot of a proxied value, but it cannot turn
 * something non constructable into a constructable type.
 *
 * - see ... [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/construct]
 * > "The `handler.construct` method is a trap for the `[[Construct]]`
 * > object internal method, which is used by operations such as the
 * > `new` operator. In order for the `new` operation to be valid on
 * > the resulting `Proxy` object, the `target` used to initialize
 * > the proxy must itself be a valid constructor."
 * @param {any} value
 * @returns {boolean}
 *  A boolean value which indicates whether the
 *  tested type can serve as constructor function.
 * @category Function Type Detection Helper
 */
export function isConstructable(value) {
  return isFunction(value) && hasOwnPrototype(value) && hasConstructSlot(value);
  // return isFunction(value) && hasConstructSlot(value);
}
 
// /**
//  * Detects whether the passed `value` is a constructable
//  * function type.
//  *
//  * It does so by testing the `[[construct]]` slot of the
//  * passed possibly constructable type, but it does not
//  * invoke it.
//  *
//  * The `construct` proxy handler is allowed to overwrite
//  * the `[[construct]]` slot of a proxied value, but
//  * it cannot turn something non constructable into
//  * a constructable type.
//  *
//  *  - see ... [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/construct]
//  *     > "The `handler.construct` method is a trap for the `[[Construct]]`
//  *     > object internal method, which is used by operations such as the
//  *     > `new` operator. In order for the `new` operation to be valid on
//  *     > the resulting `Proxy` object, the `target` used to initialize
//  *     > the proxy must itself be a valid constructor."
//  *
//  *  - Thus, it is feasible enough to let the construct trap
//  *    return an object instance.
//  *
//  * **Note:**
//  *
//  * The [[construct]] slot of either generator function type,
//  * async or not, indicates that generator functions are not
//  * constructable, despite each returning a valid instance of
//  * its type when being invoked with just the call/`()` operator.
//  *
//  * Therefore this implementation of an introspective `isConstructable`
//  * functionality recognizes generator functions as _**constructable**_.
//  *
//  * This method is recommended and **safe**.
//  *
//  * @param value{any}
//  * @returns {boolean}
//  *  A boolean value which indicates whether the
//  *  tested type can serve as a constructor function.
//  * @category Function Type Detection Helper
//  */
// export function isConstructable(value) {
//   return (
//     isAnyGeneratorFunction(value) || (isFunction(value) && hasConstructSlot(value))
//   );
// }