@typescript-package/wrapped-descriptor
Version:
A lightweight TypeScript library for wrapped property descriptor.
472 lines (462 loc) • 17.4 kB
JavaScript
import { CommonDescriptor } from '@typescript-package/descriptor';
// Abstract.
/**
* @description The abstract class for wrapped descriptors.
* @export
* @abstract
* @class WrappedDescriptorCore
* @template [O=any] The type of the object to define the descriptor on.
* @template {keyof O} [K=keyof O] The key of the object to define the descriptor on.
* @template {K extends keyof O ? O[K] : any} [V=K extends keyof O ? O[K] : any] The value type of the key in the object.
* @template {boolean} [A=boolean] The type of active.
* @template {boolean} [N=boolean] The type of enabled.
* @template {boolean} [C=boolean] The type of configurable.
* @template {boolean} [E=boolean] The type of enumerable.
* @template {WrappedDescriptorCore<O, K, V, A, N, C, E, D> | PropertyDescriptor} [D=WrappedDescriptorCore<O, K, V, A, N, C, E, any>]
* @extends {CommonDescriptor<C, E>}
* @implements {WrappedPropertyDescriptor<O, K, V, A, N, C, E, D>}
*/
class WrappedDescriptorCore extends CommonDescriptor {
/**
* @description The defaults for instance `active` property.
* @public
* @static
* @type {boolean}
*/
static active = true;
/**
* @description The defaults for instance `enabled` property.
* @public
* @static
* @type {boolean}
*/
static enabled = true;
/**
* @description The prefix for the private key.
* @public
* @static
* @type {string}
*/
static prefix = '_';
/**
* @description Wraps the property with the descriptor.
* @protected
* @param {Partial<WrappedPropertyDescriptor<O, K, V, A, N, C, E, D>>} param0 The wrapped property `set` and `get` descriptor.
* @returns {(Pick<WrappedPropertyDescriptor<O, K, V, A, N, C, E, D>, 'get' | 'set'>)}
*/
wrap({ get, set }) {
// Use descriptor instance.
const descriptor = this;
// Return the wrapped property.
return {
get: get
? (function () { return get?.call(this, descriptor); })
: (function () {
const o = this;
// Check if the descriptor is active.
const active = typeof descriptor.active === 'object' ? descriptor.active.onGet : descriptor.active;
// Check if the descriptor is enabled.
const enabled = typeof descriptor.enabled === 'boolean' ? descriptor.enabled : WrappedDescriptorCore.enabled;
// Perform previous descriptor `get`. Handle the data descriptor as first.
let previousValue = descriptor.previousDescriptor && descriptor.previousDescriptor.enabled !== false
? 'value' in descriptor.previousDescriptor
? descriptor.previousDescriptor.value
: descriptor.previousDescriptor?.get
? descriptor.previousDescriptor.get.call(o, descriptor.previousDescriptor)
: undefined
: undefined;
// Returns.
// If descriptor is enabled, return the value from the `privateKey` also by using `onGet` hook on `active` set to `true`.
if (enabled === true) {
return descriptor.onGet && active === true
? descriptor.onGet.call(o, descriptor.key, previousValue, o[descriptor.privateKey], o)
: o[descriptor.privateKey];
}
return void (0);
}),
set: set
? function (value) {
set?.call(this, value, descriptor);
}
: function (value) {
// Set the this as the target object.
const o = this;
// Check if the descriptor is active.
const active = typeof descriptor.active === 'object' ? descriptor.active.onSet : descriptor.active;
// Check if the descriptor is enabled.
const enabled = typeof descriptor.enabled === 'boolean' ? descriptor.enabled : WrappedDescriptorCore.enabled;
// Initialize `previousValue`.
let previousValue = undefined;
// Get the previous value from previous descriptor or current value.
if (enabled === true) {
previousValue = descriptor.previousDescriptor && descriptor.previousDescriptor.enabled !== false
? 'value' in descriptor.previousDescriptor
? descriptor.previousDescriptor.value
: descriptor.previousDescriptor.privateKey
? o[descriptor.previousDescriptor.privateKey]
: o[descriptor.privateKey]
: o[descriptor.privateKey];
}
// Perform previous descriptor.
descriptor.previousDescriptor?.set
&& descriptor.previousDescriptor.enabled !== false
&& descriptor.previousDescriptor.set.call(o, value, descriptor.previousDescriptor);
if (enabled === true) {
// Set the value under the `privateKey`.
o[descriptor.privateKey] = descriptor.onSet && active === true
? descriptor.onSet.call(o, value, previousValue, descriptor.key, o)
: value;
}
}
};
}
}
// Abstract.
/**
* @description The base abstraction class for plain wrapped descriptors.
* @export
* @abstract
* @class PlainWrappedDescriptorBase
* @template [O=any] The type of the object to define the descriptor on.
* @template {keyof O} [K=keyof O] The key of the object to define the descriptor on.
* @template {K extends keyof O ? O[K] : any} [V=K extends keyof O ? O[K] : any] The value type of the key in the object.
* @template {boolean} [A=boolean] The type of active.
* @template {boolean} [N=boolean] The type of enabled.
* @template {boolean} [C=boolean] The type of configurable.
* @template {boolean} [E=boolean] The type of enumerable.
* @template {PlainWrappedDescriptorBase<O, K, V, A, N, C, E, D> | PropertyDescriptor} [D=PlainWrappedDescriptorBase<O, K, V, A, N, C, E, any>]
* @extends {WrappedDescriptorCore<O, K, V, A, N, C, E, D>}
*/
class PlainWrappedDescriptorBase extends WrappedDescriptorCore {
/**
* @description The active state of the descriptor.
* @public
* @type {(A | { onGet?: boolean; onSet?: boolean })}
*/
active;
/**
* @description The enabled state of the descriptor.
* @public
* @type {N}
*/
enabled;
/**
* @description The index of the descriptor in the chain.
* @public
* @type {?number}
*/
index;
/**
* @description The optional `get` method for the descriptor.
* @public
* @type {(this: O, descriptor?: D) => V}
*/
get;
/**
* @description The key of the descriptor.
* @public
* @type {K}
*/
key;
/**
* @description The on get hook function for the descriptor.
* @public
* @type {?GetterCallback<O, K>}
*/
onGet;
/**
* @description The on set hook function for the descriptor.
* @public
* @type {?SetterCallback<O, K>}
*/
onSet;
/**
* @description The previous descriptor.
* @public
* @type {D}
*/
previousDescriptor;
/**
* @description The private key for the descriptor.
* @public
* @type {PropertyKey}
*/
privateKey;
/**
* @description The optional `set` method for the descriptor.
* @public
* @type {(this: O, value: V, descriptor?: D) => void}
*/
set;
/**
* Creates an instance of `WrappedDescriptorBase` child class.
* @constructor
* @param {O} object The object to define the descriptor on.
* @param {K} key The key of the object to define the descriptor on.
* @param {Partial<WrappedPropertyDescriptor<O, K, V, A, N, C, E, D>>} attributes The property descriptor to wrap.
*/
constructor(object, key, attributes) {
super(attributes);
delete this.index;
delete this.onGet;
delete this.onSet;
// delete this.previousDescriptor;
// Assign the properties.
// Required.
this.active = typeof attributes.active === 'boolean' || typeof attributes.active === 'object' ? attributes.active : PlainWrappedDescriptorBase.active;
this.enabled = typeof attributes.enabled === 'boolean' || typeof attributes.enabled === 'object' ? attributes.enabled : PlainWrappedDescriptorBase.enabled;
this.key = key;
this.previousDescriptor = (attributes.previousDescriptor || Object.getOwnPropertyDescriptor(object, key));
this.privateKey = attributes.privateKey || `_${String(key)}`;
// Optional.
attributes.index && (this.index = attributes.index);
attributes.onGet && (this.onGet = attributes.onGet);
attributes.onSet && (this.onSet = attributes.onSet);
// Wraps the `get` getter and `set` setter of the descriptor.
const { get, set } = super.wrap(attributes);
// Assigns the wrapped `get` getter and `set` setter to the descriptor.
this.get = get;
this.set = set;
}
}
// Abstract.
/**
* @description The base abstraction class for wrapped descriptors.
* @export
* @abstract
* @class WrappedDescriptorBase
* @template [O=any] The type of the object to define the descriptor on.
* @template {keyof O} [K=keyof O] The key of the object to define the descriptor on.
* @template {K extends keyof O ? O[K] : any} [V=K extends keyof O ? O[K] : any] The value type of the key in the object.
* @template {boolean} [A=boolean] The type of active.
* @template {boolean} [N=boolean] The type of enabled.
* @template {boolean} [C=boolean] The type of configurable.
* @template {boolean} [E=boolean] The type of enumerable.
* @template {WrappedDescriptorBase<O, K, V, A, N, C, E, D> | PropertyDescriptor} [D=WrappedDescriptorBase<O, K, V, A, N, C, E, any>]
* @extends {WrappedDescriptorCore<O, K, V, A, N, C, E, D>}
*/
class WrappedDescriptorBase extends WrappedDescriptorCore {
/**
* @inheritdoc
*/
get active() {
return this.#active;
}
/**
* @inheritdoc
*/
get enabled() {
return this.#enabled;
}
/**
* @inheritdoc
*/
get index() {
return this.#index;
}
/**
* @inheritdoc
*/
get key() {
return this.#key;
}
/**
* @inheritdoc
*/
get onGet() {
return this.#onGet;
}
/**
* @inheritdoc
*/
get onSet() {
return this.#onSet;
}
/**
* @inheritdoc
*/
get previousDescriptor() {
return this.#previous;
}
/**
* @inheritdoc
*/
get privateKey() {
return this.#privateKey;
}
/**
* @description The active state of the descriptor.
* @type {(A | {onGet?: boolean | undefined; onSet?: boolean | undefined;})}
*/
#active;
/**
* @description The enabled state of the descriptor.
* @type {N}
*/
#enabled;
/**
* @description The index of the descriptor in the chain.
* @type {?number}
*/
#index;
/**
* @description The key of the descriptor.
* @type {K}
*/
#key;
/**
* @description The on get hook function for the descriptor.
* @type {?GetterCallback<O, K>}
*/
#onGet;
/**
* @description The on set hook function for the descriptor.
* @type {?SetterCallback<O, K>}
*/
#onSet;
/**
* @description The previous descriptor in the chain.
* @type {?D}
*/
#previous;
/**
* @description The private key for the descriptor.
* @type {PropertyKey}
*/
#privateKey;
/**
* Creates an instance of `WrappedDescriptorBase` child class.
* @constructor
* @param {O} object The object to define the descriptor on.
* @param {K} key The key of the object to define the descriptor on.
* @param {Partial<WrappedPropertyDescriptor<O, K, V, A, N, C, E, D>>} [attributes={}] The property descriptor attributes.
*/
constructor(object, key, attributes = {}) {
super(attributes);
// Assign the properties.
// Required.
this.#active = typeof attributes.active === 'boolean' || typeof attributes.active === 'object' ? attributes.active : WrappedDescriptorBase.active;
this.#enabled = typeof attributes.enabled === 'boolean' || typeof attributes.enabled === 'object' ? attributes.enabled : WrappedDescriptorBase.enabled;
this.#privateKey = attributes.privateKey || `${WrappedDescriptorBase.prefix}${String(key)}`;
// Optional.
this.#index = attributes.index;
this.#key = key;
this.#onGet = attributes.onGet;
this.#onSet = attributes.onSet;
this.#previous = attributes.previousDescriptor;
}
/**
* @inheritdoc
* @public
* @returns {WrappedPropertyDescriptor<O, K, V, A, N, C, E, D>}
*/
valueOf() {
return {
active: this.active,
configurable: this.configurable,
enabled: this.enabled,
enumerable: this.enumerable,
get: this.get,
index: this.index,
onGet: this.onGet,
onSet: this.onSet,
previousDescriptor: this.previousDescriptor,
privateKey: this.privateKey,
set: this.set,
};
}
}
// Abstract.
/**
* @description The plain wrapped descriptor class.
* @export
* @class PlainWrappedDescriptor
* @template [O=any] The type of the object to define the descriptor on.
* @template {keyof O} [K=keyof O] The key of the object to define the descriptor on.
* @template {K extends keyof O ? O[K] : any} [V=K extends keyof O ? O[K] : any] The value type of the key in the object.
* @template {boolean} [A=boolean] The type of active.
* @template {boolean} [N=boolean] The type of enabled.
* @template {boolean} [C=boolean] The type of configurable.
* @template {boolean} [E=boolean] The type of enumerable.
* @template {PlainWrappedDescriptorBase<O, K, V, A, N, C, E, D> | PropertyDescriptor} [D=PlainWrappedDescriptorBase<O, K, V, A, N, C, E, any>]
*/
class PlainWrappedDescriptor extends PlainWrappedDescriptorBase {
constructor(object, key, descriptor) {
super(object, key, descriptor);
}
}
// Abstract.
/**
* @description The concrete implementation of wrapped property descriptor.
* @export
* @class WrappedDescriptor
* @template [O=any] The type of the object to define the descriptor on.
* @template {keyof O} [K=keyof O] The key of the object to define the descriptor on.
* @template {K extends keyof O ? O[K] : any} [V=K extends keyof O ? O[K] : any] The value type of the key in the object.
* @template {boolean} [A=boolean] The type of active.
* @template {boolean} [N=boolean] The type of enabled.
* @template {boolean} [C=boolean] The type of configurable.
* @template {boolean} [E=boolean] The type of enumerable.
* @template {WrappedDescriptor<O, K, V, A, N, C, E, D> | PropertyDescriptor} [D=WrappedDescriptor<O, K, V, A, N, C, E, any>] The type of the previous descriptor.
* @extends {WrappedDescriptorBase<O, K, V, A, N, C, E, D>}
*/
class WrappedDescriptor extends WrappedDescriptorBase {
/**
* @description The string tag for the descriptor.
* @public
* @readonly
* @type {string}
*/
get [Symbol.toStringTag]() {
return 'WrappedDescriptor';
}
/**
* @inheritdoc
*/
get get() {
return this.#get;
}
/**
* @inheritdoc
*/
get set() {
return this.#set;
}
/**
* @description The `get` method for the descriptor.
* @type {(this: O, descriptor?: D | undefined) => V}
*/
#get;
/**
* @description The `set` method for the descriptor.
* @type {(this: O, value: V, descriptor?: D | undefined) => void}
*/
#set;
/**
* Creates an instance of `WrappedDescriptor`.
* @constructor
* @param {O} object The object to define the descriptor on.
* @param {K} key The key of the object to define the descriptor on.
* @param {Partial<WrappedPropertyDescriptor<O, K, V, A, N, C, E, D>>} attributes
*/
constructor(object, key, attributes = {}) {
super(object, key, attributes);
// Wrap the property `get` and `set`.
const { get, set } = this.wrap({
get: attributes.get,
set: attributes.set
});
// Assign the wrapped `get` and `set` methods.
this.#get = get;
this.#set = set;
}
}
// Abstract.
/*
* Public API Surface of wrapped-descriptor
*/
/**
* Generated bundle index. Do not edit.
*/
export { PlainWrappedDescriptor, PlainWrappedDescriptorBase, WrappedDescriptor, WrappedDescriptorBase, WrappedDescriptorCore };
//# sourceMappingURL=typescript-package-wrapped-descriptor.mjs.map