@typescript-package/wrap-property
Version:
A lightweight TypeScript package for wrapping object properties.
314 lines (219 loc) • 10.1 kB
Markdown
<a href="https://www.typescriptlang.org/">
<img
src="https://avatars.githubusercontent.com/u/189666396?s=150&u=9d55b1eb4ce258974ead76bf07ccf49ef0eb0ea7&v=4"
title="typescript-package/wrap-property - A lightweight TypeScript package with for wrapping object properties."
/>
</a>
## typescript-package/wrap-property
<!-- npm badge -->
[![npm version][typescript-package-npm-badge-svg]][typescript-package-npm-badge]
[![GitHub issues][typescript-package-badge-issues]][typescript-package-issues]
[![GitHub license][typescript-package-badge-license]][typescript-package-license]
A **lightweight** TypeScript package for wrapping object properties.
## Table of contents
- [Installation](#installation)
- [Api](#api)
- [`WrapProperty`](#wrapproperty)
- [Contributing](#contributing)
- [Support](#support)
- [Code of Conduct](#code-of-conduct)
- [Git](#git)
- [Commit](#commit)
- [Versioning](#versioning)
- [License](#license)
## Installation
### 1, Install peer dependencies
```bash
npm i @typedly/callback@1.0.0-beta.4 @typedly/controller@0.0.1-beta @typedly/descriptor@5.1.0-beta --save-peer
```
### 2. Install the package
```bash
npm install @typescript-package/wrap-property --save-peer
```
## Api
```typescript
import {
// Class.
WrapProperty, // Concrete class to wrap the property.
// Abstract.
WrapPropertyBase, // Foundational class for extension.
WrapPropertyCore, // The abstraction for base.
} from '@typescript-package/wrap-property';
```
### Exports
- `WrapProperty` — Concrete class for wrapping properties on objects/classes.
- `WrapPropertyBase` — Base abstraction to extend for custom wrappers.
- `WrapPropertyCore` — The core abstraction.
### `WrapProperty` Constructor
```typescript
new WrapProperty(target, key, descriptor?)
```
- `target`: The object or class whose property is to be wrapped.
- `key`: The property key.
- `descriptor` (optional): Options such as `onGet`, `onSet`, `privateKey`, `configurable`, and `enumerable`.
---
### `WrapProperty`
### Usage
### 1. Basic Property Wrapping
This example demonstrates how to intercept reads and writes for a property.
```typescript
import { WrapProperty } from '@typescript-package/wrap-property';
class MyClass {
myProp = 'hello';
}
const inst = new MyClass();
export const wrapped = new WrapProperty(inst, 'myProp', {
active: true as boolean,
enabled: true as boolean,
onGet(key, previousValue, value) {
console.log(
`Accessed ${String(key)}`,
previousValue,
'→',
value
);
return value;
}
});
console.debug(`controller:`, wrapped.controller); // undefined, as no controller is set.
console.debug(`descriptor:`, wrapped.descriptor); // {active: true, configurable: true, enumerable: false, previousDescriptor: {…}, privateKey: '_myProp', …}
inst.myProp; // Logs: Accessed myProp hello → hello
wrapped.descriptor.enabled = false; // Disable the descriptor.
console.debug(inst.myProp); // undefined
wrapped.descriptor.enabled = true; // Enable the descriptor.
wrapped.descriptor.active = false; // Disable the active to not execute the `onGet` and `onSet` callbacks.
console.debug(inst.myProp); // hello
```
### 2. Wrapping a Property on a Class prototype
You can pass a class constructor to wrap its prototype property:
```typescript
import { WrapProperty } from '@typescript-package/wrap-property';
export class Example {
public static readonly staticValue = 42;
public value = 1;
}
// Wrap the "value" property to log whenever it is accessed or set.
export const wrapped1 = new WrapProperty(
Example.prototype,
'value',
{
onGet(key, previousValue, value) {
console.log(`Getting "${String(key)}":`, value);
return value;
},
onSet(value, previousValue, key) {
console.log(`Setting "${String(key)}":`, previousValue, '→', value);
return value;
},
}
);
const example = new Example();
delete (example as any).value; // Remove the own property to ensure the wrap works.
example.value = 42; // Logs: Setting "value": 1 → 42
example.value; // Logs: Getting "value": 42
```
### 3. Customizing with WrapPropertyBase
You can extend `WrapPropertyBase` to implement your own custom behavior:
```typescript
import { WrapPropertyBase } from '@typescript-package/wrap-property';
class CustomLogger extends WrapPropertyBase<typeof obj, 'someKey'> {
constructor(object: typeof obj, key: 'someKey') {
super(object, key, {
onGet: (k, v) => {
console.log(`Custom get for ${String(k)}: ${v}`);
return v;
}
});
super.wrap(object, key); // wrap.
}
}
const obj = { someKey: 123 };
new CustomLogger(obj, 'someKey');
obj.someKey; // Logs: Custom get for someKey: 123
```
### 4. Multiple wrap and one unwrap
```typescript
import { WrapProperty } from '@typescript-package/wrap-property';
export class TestObject {
firstName = 'First name';
lastName = 'Last name';
age = 1000;
constructor(param11: number, param22: string) {}
}
const object = new TestObject(0, 'test');
let wrappedAgeFirst = new WrapProperty(object, 'age', { privateKey: '_age' });
let wrappedAgeSecond = new WrapProperty(object, 'age', { privateKey: '__age' });
let wrappedAgeThird = new WrapProperty(object, 'age', { privateKey: '___age' });
wrappedAgeSecond.unwrap(); // Unwraps the last, the field __age is not connected.
object.age = 3000;
console.debug(object.age); // 3000
```
### 4. Wrapping with custom controller
```typescript
import { WrapProperty } from '@typescript-package/wrap-property';
export class TestObject {
firstName = 'First name';
lastName = 'Last name';
age = 1000;
constructor(param11: number, param22: string) {}
}
```
## Contributing
Your contributions are valued! If you'd like to contribute, please feel free to submit a pull request. Help is always appreciated.
## Support
If you find this package useful and would like to support its and general development, you can contribute through one of the following payment methods. Your support helps maintain the packages and continue adding new.
Support via:
- [Stripe](https://donate.stripe.com/dR614hfDZcJE3wAcMM)
- [Revolut](https://checkout.revolut.com/pay/048b10a3-0e10-42c8-a917-e3e9cb4c8e29)
- [GitHub](https://github.com/sponsors/angular-package/sponsorships?sponsor=sciborrudnicki&tier_id=83618)
- [DonorBox](https://donorbox.org/become-a-sponsor-to-the-angular-package?default_interval=o)
- [Patreon](https://www.patreon.com/checkout/angularpackage?rid=0&fan_landing=true&view_as=public)
or via Trust Wallet
- [XLM](https://link.trustwallet.com/send?coin=148&address=GAFFFB7H3LG42O6JA63FJDRK4PP4JCNEOPHLGLLFH625X2KFYQ4UYVM4)
- [USDT (BEP20)](https://link.trustwallet.com/send?coin=20000714&address=0xA0c22A2bc7E37C1d5992dFDFFeD5E6f9298E1b94&token_id=0x55d398326f99059fF775485246999027B3197955)
- [ETH](https://link.trustwallet.com/send?coin=60&address=0xA0c22A2bc7E37C1d5992dFDFFeD5E6f9298E1b94)
- [BTC](https://link.trustwallet.com/send?coin=0&address=bc1qnf709336tfl57ta5mfkf4t9fndhx7agxvv9svn)
- [BNB](https://link.trustwallet.com/send?coin=20000714&address=0xA0c22A2bc7E37C1d5992dFDFFeD5E6f9298E1b94)
## Code of Conduct
By participating in this project, you agree to follow **[Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/)**.
## GIT
### Commit
- [AngularJS Git Commit Message Conventions][git-commit-angular]
- [Karma Git Commit Msg][git-commit-karma]
- [Conventional Commits][git-commit-conventional]
### Versioning
[Semantic Versioning 2.0.0][git-semver]
**Given a version number MAJOR.MINOR.PATCH, increment the:**
- MAJOR version when you make incompatible API changes,
- MINOR version when you add functionality in a backwards-compatible manner, and
- PATCH version when you make backwards-compatible bug fixes.
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
**FAQ**
How should I deal with revisions in the 0.y.z initial development phase?
> The simplest thing to do is start your initial development release at 0.1.0 and then increment the minor version for each subsequent release.
How do I know when to release 1.0.0?
> If your software is being used in production, it should probably already be 1.0.0. If you have a stable API on which users have come to depend, you should be 1.0.0. If you’re worrying a lot about backwards compatibility, you should probably already be 1.0.0.
## License
MIT © typescript-package ([license][typescript-package-license])
<!-- This package: typescript-package -->
<!-- GitHub: badges -->
[typescript-package-badge-issues]: https://img.shields.io/github/issues/typescript-package/wrap-property
[typescript-package-badge-forks]: https://img.shields.io/github/forks/typescript-package/wrap-property
[typescript-package-badge-stars]: https://img.shields.io/github/stars/typescript-package/wrap-property
[typescript-package-badge-license]: https://img.shields.io/github/license/typescript-package/wrap-property
<!-- GitHub: badges links -->
[typescript-package-issues]: https://github.com/typescript-package/wrap-property/issues
[typescript-package-forks]: https://github.com/typescript-package/wrap-property/network
[typescript-package-license]: https://github.com/typescript-package/wrap-property/blob/master/LICENSE
[typescript-package-stars]: https://github.com/typescript-package/wrap-property/stargazers
<!-- This package -->
<!-- Package: typescript-package -->
<!-- npm -->
[typescript-package-npm-badge-svg]: https://badge.fury.io/js/@typescript-package%2Fwrap-property.svg
[typescript-package-npm-badge]: https://badge.fury.io/js/@typescript-package%2Fwrap-property
<!-- GIT -->
[git-semver]: http://semver.org/
<!-- GIT: commit -->
[git-commit-angular]: https://gist.github.com/stephenparish/9941e89d80e2bc58a153
[git-commit-karma]: http://karma-runner.github.io/0.10/dev/git-commit-msg.html
[git-commit-conventional]: https://www.conventionalcommits.org/en/v1.0.0/