Decorators for everyday programming.
In this library you'll find a collection of useful shorthands for boosting productivity through the stage 3 Decorators API.
npm i @figliolia/decorators
# or
yarn add @figliolia/decorators
- Bound
- Cache
- Debounce
- Throttle
- Animation Frame
- Log Browser
- Log Server
- Measure Browser
- Measure Server
- Unsafe Chainable
Implicitely bind class methods to their instances
import { bound } from "@figliolia/decorators";
class MyClass {
private someField = "value";
@bound
public someMethod() {
return this.someField;
}
}
const { someMethod } = new MyClass();
// Works
someMethod();
Cache previous calls to a given method
import { cache } from "@figliolia/decorators";
class MyClass {
@cache
public expensive(x: number, y: number) {
// Compute expensive value
}
}
const instance = new MyClass();
// computes on first execution
instance.expensive(1, 2);
// reads from cache then onwards as long as arguments match
instance.expensive(1, 2);
Debounces calls to a given method for a pre-determined duration
import { debounce } from "@figliolia/decorators";
class MyClass {
@debounce(300)
public getData(query: string) {
void fetch(`/api/data?query=${query}`);
}
}
const instance = new MyClass();
// Invokes 300ms following the last call
void instance.getData("searching for something");
Throttles calls to a given method for a pre-determined duration
import { throttle } from "@figliolia/decorators";
class MyClass {
@throttle(300)
public getData(query: string) {
void fetch(`/api/data?query=${query}`);
}
}
const instance = new MyClass();
// Invokes immediately, then disables later calls for 300ms
void instance.getData("searching for something");
Invokes the target method using calls to requestAnimationFrame
import { animationFrame } from "@figliolia/decorators";
class MyClass {
node: HTMLElement;
constructor(ID: string) {
this.node = document.getElementById(ID);
}
@animationFrame
public animate() {
const { translate } = this.node.style;
const current = parseInt(translate.slice(0, -2));
if(current === 100) {
return;
}
this.node.style.translate = `${current + 1}px`;
this.animate()
}
}
const instance = new MyClass("elementID");
// animates an element's translateX property
instance.animate();
Logs invokation arguments and return values for a given method as long as the NODE_ENV
is not production
This method will use native colorized logging (in chrome and firefox)
import { logBrowser } from "@figliolia/decorators";
class MyClass {
@logBrowser
public async getData(query: string) {
return fetch(`/api/data?query=${query}`);
}
}
const instance = new MyClass();
void instance.getData("searching for something");
Logs invokation arguments and return values for a given method as long as the NODE_ENV
is not production
This method will use Chalk for colorized logging
import { logServer } from "@figliolia/decorators";
class MyClass {
@logServer
public async getData(query: string) {
return fetch(`/api/data?query=${query}`);
}
}
const instance = new MyClass();
void instance.getData("searching for something");
Logs the duration occupied by the target method at runtime
import { measureBrowser } from "@figliolia/decorators";
class MyClass {
@measureBrowser
public expensive() {
for(let i = 0; i < 1_000_000; i++) {
}
}
}
const instance = new MyClass();
instance.expensive();
Logs the duration occupied by the target method at runtime
import { measureServer } from "@figliolia/decorators";
class MyClass {
@measureServer
public expensive() {
for(let i = 0; i < 1_000_000; i++) {
}
}
}
const instance = new MyClass();
instance.expensive();
Overrides the return type of a given method, replacing it with the class instance
import { chainable } from "@figliolia/decorators";
class MyClass {
@chainable
public async method1() {
}
@chainable
public async method2() {
}
}
const instance = new MyClass().method1().method(2);
// instance = MyClass
If you're a typescript developer, the Experimental decorators API (found under the experimentalDecorators
flag) has likely become familiar to you in recent years. While it's not far from the JavaScript Stage 3 Proposal, there are significant syntactical mismatches between each decorators implementation that make interoperability impossible.
Therefore, this library will not use TypeScript's experimental implementation, and instead favor JavaScript's Stage 3 Proposal. To read more on the Stage 3 proposal and where it varies from experimental decorators, I'd recommend this article.
Typescipt is limited in it's ability to transform types based on the presence of a decorator. This means, that if you modify argments or a return type in a decorator, Typescript will not know about it (yet). For the time-being (if you're a typescript user), I'd recommend not mutating method or property declarations in your decorators, and instead limit your usage functional transforms that maintain your application's type safety.
Using this library without a syntax transform will not work any browser or node.js at the time of writing (August 12th, 2024). To ensure your code works smoothly across varying runtimes, check out the Babel's Transform for Stage 3 Decorators.