@figliolia/decorators
TypeScript icon, indicating that this package has built-in type declarations

2.0.2 • Public • Published

Decorators

Decorators for everyday programming.

In this library you'll find a collection of useful shorthands for boosting productivity through the stage 3 Decorators API.

Installation

npm i @figliolia/decorators
# or 
yarn add @figliolia/decorators

API

  1. Bound
  2. Cache
  3. Debounce
  4. Throttle
  5. Animation Frame
  6. Log Browser
  7. Log Server
  8. Measure Browser
  9. Measure Server
  10. Unsafe Chainable

Bound

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

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);

Debounce

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");

Throttle

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");

Animation Frame

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();

Log Browser

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");

Log Server

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");

Measure Browser

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();

Measure Server

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();

Unsafe Chainable

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

Stage 3 Decorator Proposal Vs. Experimental Decorators

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.

Limitations

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.

For JavaScript Users

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.

Package Sidebar

Install

npm i @figliolia/decorators

Weekly Downloads

5

Version

2.0.2

License

MIT

Unpacked Size

41.9 kB

Total Files

56

Last publish

Collaborators

  • alexfigliolia