@typescript-package/descriptor
Version:
A lightweight TypeScript library for property descriptor.
522 lines (513 loc) • 19.4 kB
JavaScript
/**
* @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