/**
* @module fjlMutable
* @note Custom jsdoc type definitions defined toward end of file.
*/
import {isUndefined, curry, apply, isType, errorIfNotType} from 'fjl';
/**
* Creates `defineProps` and `defineEnumProps` methods based on `{enumerable}` param.
* @param {{enumerable: Boolean}}
* @returns {function(*, *)|PropsDefinerCall}
* @private
*/
function createDefinePropsMethod ({enumerable}) {
const operation = enumerable ? defineEnumProp : defineProp;
return (argTuples, target) => {
argTuples.forEach(argTuple => {
const [TypeRef, propName, defaultValue] = argTuple;
apply(operation, [TypeRef, target, propName, defaultValue]);
});
return target;
};
}
export const
/**
* Creates a descriptor for a property which is settable but throws
* errors when the `Type` is disobeyed.
* @function module:fjlMutable.createTypedDescriptor
* @param Type {TypeRef} - {String|Function}
* @param target {*}
* @param propName {String}
* @returns {Descriptor} - Property descriptor with just getter and setter.
*/
createTypedDescriptor = (Type, target, propName) => {
let _value;
return {
get: function () {
return _value;
},
set: function (value) {
_value = errorIfNotType(Type, propName, target, value);
}
};
},
/**
* Returns a target-descriptor tuple whose 'descriptor' will be set to
* enumerable (`enumerable: true`).
* @function module:fjlMutable.toEnumerableDescriptor
* @param {TargetDescriptorTuple} - [target, descriptor] tuple.
* @returns {TargetDescriptorTuple} - Array of target and descriptor.
*/
toEnumerableDescriptor = ([target, descriptor]) => {
descriptor.enumerable = true;
return [target, descriptor];
},
/**
* Returns an target and descriptor tuple from given.
* @function module:fjlMutable.toTargetDescriptorTuple
* @param targetOrTargetDescriptorTuple {(*|Array<*, *>)} - Target object or tuple of target and descriptor.
* @returns {(Array<*>|Array<*,*>)}
*/
toTargetDescriptorTuple = targetOrTargetDescriptorTuple =>
isType('Array', targetOrTargetDescriptorTuple) ? // Strict type check for array
targetOrTargetDescriptorTuple : [targetOrTargetDescriptorTuple],
/**
* Allows you to define a "typed" property on given `target`.
* @function module:fjlMutable.defineProp
* @param Type {TypeRef} - {String|Function}
* @param target {TargetDescriptorTuple} - Target or array of target and descriptor ([target, descriptor]).
* @param propName {String}
* @param [defaultValue=undefined] {*}
* @returns {TargetDescriptorTuple}
*/
defineProp = (Type, target, propName, defaultValue = undefined) => {
const [_target, _descriptor] = toTargetDescriptorTuple(target),
descriptor = _descriptor || createTypedDescriptor(Type, _target, propName);
Object.defineProperty(_target, propName, descriptor);
if (!isUndefined(defaultValue)) {
_target[propName] = defaultValue;
}
return [_target, descriptor];
},
/**
* Allows you to define a "typed", enumerated property on `target`.
* @function module:fjlMutable.defineEnumProp
* @param Type {TypeRef} - {String|Function}
* @param target {TargetDescriptorTuple} - Target or array of target and descriptor ([target, descriptor]).
* @param propName {String}
* @param [defaultValue=undefined] {*}
* @returns {TargetDescriptorTuple}
*/
defineEnumProp = (Type, target, propName, defaultValue = undefined) => {
const [_target, _descriptor] = toTargetDescriptorTuple(target),
descriptor = _descriptor || createTypedDescriptor(Type, _target, propName);
return defineProp(
Type,
toEnumerableDescriptor([_target, descriptor]),
propName,
defaultValue
);
},
/**
* Allows you to define multiple enum props at once on target.
* @function module:fjlMutable.defineEnumProps
* @param argsTuple {Array.<DefinePropArgsTuple>} - Array of argArrays for `defineEnumProp`.
* @param [target = undefined] {Target} - Target to use in internal calls if one is not provided but encountered 'argArray'.
* @returns {Array.<TargetDescriptorTuple>} - Results of each call to `defineEnumProp`.
*/
defineEnumProps = curry(createDefinePropsMethod({enumerable: true})),
/**
* Allows you to define multiple props at once on target.
* @function module:fjlMutable.defineProps
* @param argsTuple {Array.<DefinePropArgsTuple>} - Array of argArrays for `defineProp`.
* @param [target = undefined] {Target} - Target to use in internal calls if one is not provided but encountered 'argArray'.
* @returns {Array.<TargetDescriptorTuple>} - Results of each call to `defineProp`.
* @curried
*/
defineProps = curry(createDefinePropsMethod({enumerable: false}))
;
/** ============================================================= */
/** Type definitions: */
/** ============================================================= */
/**
* @typedef {String|Function} TypeRef
* @description Type reference. Either actual type or type's name; E.g., `Type.name`
*/
/**
* @typedef {*} Target
*/
/**
* @typedef {Object} Descriptor
*/
/**
* @typedef {Array<Target, Descriptor>} TargetDescriptorTuple
*/
/**
* @typedef {Array.<TypeRef, TargetDescriptorTuple, String, *>} DefinePropArgsTuple
* @description Arguments list for `defineProp` and/or `defineEnumProp` (note: some
* parts of array/tuple are options (namely the last two args)); E.g.,
* ```
* [String, [someTarget], 'somePropName', 'someDefaultValue] // ...
* ```
*/
/**
* @typedef {Function} PropsDefinerCall
* @description Same type as `defineProp` and `defineEnumProp`
* @param argsTuple {DefinePropArgsTuple}
* @param target {Target}
* @returns {Array.<TargetDescriptorTuple>}
*/