UNPKG

@typescript-package/descriptor

Version:

A lightweight TypeScript library for property descriptor.

522 lines (513 loc) 19.4 kB
/** * @description * @export * @abstract * @class CommonDescriptor */ class CommonDescriptor { /** * @description The default value for configurable. * @public * @static * @type {?boolean} */ static configurable; // /** * @description The default value for enumerable. * @public * @static * @type {?boolean} */ static enumerable; //#region Property descriptor /** * @description * @public * @type {?boolean} */ configurable; /** * @description * @public * @type {?boolean} */ enumerable; //#endregion /** * Creates an instance of child class. * @constructor * @param {Pick<PropertyDescriptor, 'configurable' | 'enumerable'>} [param0={}] * @param {Pick<PropertyDescriptor, "configurable" | "enumerable">} param0.configurable * @param {Pick<PropertyDescriptor, "configurable" | "enumerable">} param0.enumerable */ constructor({ configurable, enumerable } = {}) { delete this.configurable, delete this.enumerable; typeof configurable === 'boolean' ? (this.configurable = configurable) : typeof CommonDescriptor.configurable === 'boolean' && (this.configurable = CommonDescriptor.configurable); typeof enumerable === 'boolean' ? (this.enumerable = enumerable) : typeof CommonDescriptor.enumerable === 'boolean' && (this.enumerable = CommonDescriptor.enumerable); } } // Abstract, /** * @description * @export * @class AccessorDescriptor * @template {object} [Obj=object] * @template {keyof Obj} [PropertyName=keyof Obj] * @template [Value=Obj[PropertyName]] * @extends {CommonDescriptor} */ class AccessorDescriptor extends CommonDescriptor { /** * @description Returns strictly defined accessor descriptor of a `ThisAccessorPropertyDescriptor<Value, Obj>` type on `get` or `set` property detected. * @param descriptor An `object` of a `ThisAccessorPropertyDescriptor<Value, Obj>` type, to define with the default values of the * `CommonDescriptor`. * @param onGuard A `ValidationCallback` function to handle the result of the check whether the `descriptor` is an `object` * with `get` or `set` property, by default it uses `accessorCallback()` function. * @throws Throws an error if the descriptor is not an object of a `ThisAccessorPropertyDescriptor<Value, Obj>` type, which means it * doesn't contain `get` or `set` property. * @returns The returned value is an `object` of a `ThisAccessorPropertyDescriptor<Value, Obj>` type. */ static define(descriptor, onGuard) { const { configurable, enumerable, get, set } = descriptor; return this.guard(descriptor, onGuard) ? { ...{ configurable: AccessorDescriptor.configurable, enumerable: AccessorDescriptor.enumerable, }, ...{ configurable, enumerable, get, set } } : undefined; } /** * @description Guards the `descriptor` to be an `object` of a `ThisAccessorPropertyDescriptor<Value, Obj>` type. * @param descriptor The object of a `ThisAccessorPropertyDescriptor<Value, Obj>` type to guard. * @param callbackFn A `ValidationCallback` function to handle the result of the check whether or not the descriptor is an `object` * containing the `get` or `set` property. * @throws Throws an error if the descriptor is not an object of a `ThisAccessorPropertyDescriptor<Value, Obj>` type, which means it * doesn't contain `get` or `set` property. * @returns The return value is a boolean indicating whether the `descriptor` is an `object` with the `get` or `set` property. */ static guard(descriptor, callbackFn) { return callbackFn?.(typeof descriptor === 'object' && ('get' in descriptor || 'set' in descriptor), descriptor) || false; } //#region Accessor descriptor. /** * @description * @public * @type {?() => Value} */ get; /** * @description * @public * @type {?(value: Value) => void} */ set; //#endregion /** * Creates an instance of `AccessorDescriptor`. * @constructor * @param {AccessorPropertyDescriptor<Value>} [param0={}] * @param {AccessorPropertyDescriptor<Value>} param0.get * @param {AccessorPropertyDescriptor<Value>} param0.set * @param {?Obj} [object] * @param {?PropertyName} [key] */ constructor({ configurable, enumerable, get, set } = {}, object, key) { super({ configurable, enumerable }); delete this.get, delete this.set; typeof get === 'function' && (this.get = get); typeof set === 'function' && (this.set = set); } } // Abstract. /** * @description * @export * @class DataDescriptor * @template Value */ class DataDescriptor extends CommonDescriptor { /** * Returns strictly defined data descriptor of a `DataPropertyDescriptor<Value>` interface on `writable` or `value` property detected. * Strictly means, parameter `descriptor` is type guarded and method picks `configurable`, `enumerable`, `writable`, `value` * properties from the provided `descriptor` object. * @param descriptor An `object` of a `DataPropertyDescriptor<Value>` interface, to set with the default values of the * `CommonDescriptor`. * @param onValidate An optional `ResultCallback` function to handle the result of the check whether or not the `descriptor` is an `object` * with the `writable` or `value` property, by default it uses `dataCallback()` function from the static `guard()` method. * @returns The return value is an `object` of a `DataPropertyDescriptor<Value>` interface. */ static define(descriptor, onValidate) { const { configurable, enumerable, value, writable } = descriptor; return this.guard(descriptor, onValidate) ? { ...{ configurable: CommonDescriptor.configurable, enumerable: DataDescriptor.enumerable, writable: DataDescriptor.writable, }, ...{ configurable, enumerable, value, writable } } : undefined; } /** * @description Guards the `descriptor` to be an `object` of a `DataPropertyDescriptor<Value>` interface. * @param descriptor Object of a `DataPropertyDescriptor<Value>` interface to guard. * @param callbackFn A `ResultCallback` function to handle the result of the check whether or not the `descriptor` * is an `object` with the `writable` or `value` property, by default it uses `dataCallback()` function. * @throws Throws an error if the `descriptor` is not an `object` of a `DataPropertyDescriptor<Value>` interface, which means doesn't * contain `writable` or `value` property. * @returns The return value is a `boolean` indicating whether the `descriptor` is an `object` with the `writable` or `value` property. */ static guard(descriptor, callbackFn) { return typeof callbackFn === 'function' ? callbackFn(typeof descriptor === 'object' && 'value' in descriptor, descriptor) : false; } /** * @description Default writable. * @public * @static * @type {?boolean} */ static writable; /** * @description * @public * @type {?Value} */ value; /** * @description * @public * @type {?boolean} */ writable; /** * Creates an instance of `DataDescriptor`. * @constructor * @param {DataPropertyDescriptor<Value>} [param0={}] * @param {DataPropertyDescriptor<Value>} param0.configurable * @param {DataPropertyDescriptor<Value>} param0.enumerable * @param {DataPropertyDescriptor<Value>} param0.value * @param {DataPropertyDescriptor<Value>} param0.writable */ constructor({ configurable, enumerable, value, writable } = {}) { super({ configurable, enumerable }); delete this.writable; typeof writable === 'boolean' && (this.writable = writable); this.value = value; } } // Abstract. /** * @description * @export * @class Descriptor * @template {object} [Obj=object] * @template {keyof Obj} [PropertyName=keyof Obj] * @template [Value=Obj[PropertyName]] */ class Descriptor extends CommonDescriptor { /** * @description Returns accessor descriptor of a `ThisAccessorPropertyDescriptor<Value, Obj>` type, on `get` or `set` property detected. * @param descriptor An `object` of a `ThisAccessorPropertyDescriptor<Value, Obj>` type, to define with the default values of the * `CommonDescriptor`. * @param onValidate An optional `ValidationCallback` function to handle the result of the check whether or not the `descriptor` is an * `object` with `get` or `set` property, by default it uses `accessorCallback()` function from the `guard`. * @throws Throws an `Error` if the `descriptor` is not an `object` of a `ThisAccessorPropertyDescriptor<Value, Obj>` type, * which means it doesn't contain `get` or `set` property. * @returns The return value is an `object` of a `ThisAccessorPropertyDescriptor<Value, Obj>` type. */ static defineAccessor(descriptor, onValidate) { return AccessorDescriptor.define(descriptor, onValidate); } /** * @description Returns data descriptor of a `DataPropertyDescriptor<Value>` interface, on `writable` or `value` property detected. * @param descriptor An `object` of a `DataPropertyDescriptor<Value>` interface, to set with the default values of the * `CommonDescriptor`. * @param onValidate An optional `ValidationCallback` function to handle the result of the check whether or not the `descriptor` is an `object` * with the `writable` or `value` property, by default it uses `dataCallback()` function from the static `DataPropertyDescriptors.guard()` method. * @returns The return value is an `object` of a `DataPropertyDescriptor<Value>` interface. */ static defineData(descriptor, onValidate) { return DataDescriptor.define(descriptor, onValidate); } /** * @description Returns property descriptors from the specified object and its prototype. * @param object An `object` of a generic `Obj` type to get property descriptors. * @returns The return value is an `object` of a `ObjectPropertyDescriptors<Obj>` type. */ static fromObject(object) { return { ...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(object)) || {}, // ['__proto__'] equivalent to getPrototypeOf() ...Object.getOwnPropertyDescriptors(object) || {}, }; } /** * Returns property descriptor from the `object` or `class` prototype. * Wrapper function for the `getOwnPropertyDescriptor`, which "Gets the own property descriptor of the specified object." * @param object An `object` of a generic `Obj` type or a class to get own property descriptor with the specified `key`. * If `class` is provided then it uses its prototype to get the property descriptor. * @param key A `keyof Obj` value to get property descriptor from the `object`. * @returns The return value is an `object` of a `PropertyDescriptor` interface or an `undefined`. */ static fromProperty(object, key) { return (Object.getOwnPropertyDescriptor(object, key) || Object.getOwnPropertyDescriptor(Object.getPrototypeOf(object), key)); } /** * @alias fromProperty() */ static get(object, name) { return this.fromProperty(object, name); } /** * @alias fromObject() */ static getAll(object) { return this.fromObject(object); } /** * * @param object * @param names * @returns */ static pick(object, ...names) { // Prepare constant to assign descriptors of picked keys. const pickedDescriptors = {}; // Get all descriptors. const descriptors = this.getAll(object); // If descriptors exists then set picked descriptor into the map storage. typeof descriptors === 'object' && Object.keys(descriptors) .filter(key => names.includes(key)) .forEach(key => Object.assign(pickedDescriptors, { [key]: descriptors[key], })); return pickedDescriptors; } /** * The static getter accessor to define `accessor` and `data` descriptor. * @returns The returned value is an `object` with `accessor` and `data` properties. */ static get define() { return { accessor: this.defineAccessor, data: this.defineData }; } /** * The static getter accessor to get descriptors from property or object. * @returns The returned value is an `object` with `object` and `property` properties. */ static get from() { return { object: this.fromObject, property: this.fromProperty, }; } //#region Property descriptor /** * @description * @public * @type {?() => Value} */ get; /** * @description * @public * @type {?(value: Value) => void} */ set; /** * @description * @public * @type {?Value} */ value; /** * @description * @public * @type {?boolean} */ writable; //#endregion /** * Creates an instance of `Descriptor`. * @constructor * @param {AnyPropertyDescriptor<Value, Obj>} [param0={}] * @param {AnyPropertyDescriptor<Value, Obj>} param0.configurable * @param {AnyPropertyDescriptor<Value, Obj>} param0.enumerable * @param {AnyPropertyDescriptor<Value, Obj>} param0.get * @param {AnyPropertyDescriptor<Value, Obj>} param0.set * @param {AnyPropertyDescriptor<Value, Obj>} param0.value * @param {AnyPropertyDescriptor<Value, Obj>} param0.writable * @param {?Obj} [object] * @param {?PropertyName} [key] */ constructor({ configurable, enumerable, get, set, value, writable } = {}, object, key) { super({ configurable, enumerable }); // Deletes the PropertyDescriptor properties. delete this.get, delete this.set, delete this.value, delete this.writable; get && (this.get = get); set && (this.set = set); value && (this.value = value); writable && (this.writable = writable); } } // Class. /** * @description * @export * @class Descriptors * @template {object} Obj * @template {keyof Obj} Keys */ class Descriptors { /** * @description Accessor to get stored property descriptors. * @returns The returned value is descriptors of map type. */ get descriptors() { return this.#descriptors; } /** * @description Privately stored property descriptors. */ #descriptors = new Map(); /** * */ #object; /** * @description Creates an instance of `Descriptors` with obj and `keys` to pick descriptors. * @param object An object from which descriptors are retrieved. * @param keys Optional property keys to retrieve from specified `object`. */ constructor(object, ...keys) { this.#object = object; Array.isArray(keys) && keys.length > 0 ? this.setPicked(...keys) : this.setAll(); } /** * @description Get property descriptor from `#descriptors`. * @param key * @returns */ get(key) { return this.#descriptors.get(key); } /** * @description Get all descriptors from `#descriptors`. * @returns The returned value is array of all stored descriptors. */ getAll() { return Array.from(this.#descriptors.entries()); } /** * @description Check whether `#descriptors` has `key`. * @param key The `key` to check whether descriptors has. * @returns The returned value is a `boolean` indicating whether descriptors has descriptor of property `key`. */ has(key) { return this.#descriptors.has(key); } /** * @description The method sets the `value` under `key` in `#descriptors`. * @param key The property key to set descriptor. * @param value Property descriptor to set under the `key`. * @returns The returned value is an instance of `Descriptors`. */ set(key, value) { this.#descriptors.set(key, value); return this; } /** * @description The method sets all descriptors from `object`. // * @param object The object from which all descriptors are set. * @returns The returned value is an instance of this. */ setAll() { // Pick all the descriptors of the given `object`. const objectDescriptors = Descriptor.getAll(this.#object); // If description exists in the object set them into the map storage. typeof objectDescriptors === 'object' && Object.keys(objectDescriptors).forEach((key) => this.#descriptors.set(key, objectDescriptors[key])); return this; } /** * @description The method sets descriptors from `object` of `keys`. * @param object An object from which descriptors are set to `#descriptors`. * @param keys Keys of `object` to retrieved descriptors. * @returns The returned value is an instance of `Descriptors`. */ setPicked(...keys) { // Pick the descriptors of the given `keys`. const pickedDescriptors = Descriptor.pick(this.#object, ...keys); // If description exists in the object set them into the map storage. typeof pickedDescriptors === 'object' && Object.keys(pickedDescriptors).forEach(key => this.#descriptors.set(key, pickedDescriptors[key])); return this; } } // Class. class PropertyDescriptorChain { get descriptor() { return this.#descriptor; } get size() { return this.#descriptor.length; } #descriptor = new Array(); #key; #object; /** * Creates an instance of `PropertyDescriptorChain`. * @param object * @param key */ constructor(object, key) { if (!object || typeof object !== 'object') { throw new TypeError('Invalid object provided.'); } if (!key) { throw new TypeError('Invalid key provided.'); } this.#key = key; this.#object = object; this.add(); } add() { const descriptor = Descriptor.fromProperty(this.#object, this.#key); if (descriptor) { this.#descriptor.push(descriptor); } else { throw new Error(`Descriptor not found for key: ${String(this.#key)}`); } } get(id) { return this.#descriptor[id]; } last() { return this.#descriptor[this.#descriptor.length - 1]; } } /* * Public API Surface of descriptor */ /** * Generated bundle index. Do not edit. */ export { AccessorDescriptor, CommonDescriptor, DataDescriptor, Descriptor, Descriptors, PropertyDescriptorChain }; //# sourceMappingURL=typescript-package-descriptor.mjs.map