Code Monkey home page Code Monkey logo

decorate-all's Introduction

decorate-all

A typescript decorator that allows you to apply another decorator to all methods of a class.

npm i decorate-all

Note: since version 1.1.0, this package uses the reflect-metadata package as a peer dependency, so you need to have it installed.

Why

A need for this decorator arose from having to decorate all methods (route handlers) of a controller class with the same parameter decorator to apply the framework-specific metadata. In the project, which is a multi-tenant application, most routes contain the same tenantId path parameter and repeating it over each method turned out to be tedious and error prone.

This decorator is, however, not limited to adding simple metadata decorators. Another use case can be applying a logging decorator (that actually alters the method's implementation) on all methods of a class for debugging or for tracing purposes.

Usage

import { DecorateAll } from 'decorate-all';

const Uppercase = (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor,
) => {
    const original = descriptor.value;
    descriptor.value = function () {
        return original.call(this).toUpperCase();
    };
};

// instead of decorating each method on its own...
class Hello1 {
    @Uppercase
    world() {
        return 'world';
    }

    @Uppercase
    galaxy() {
        return 'galaxy';
    }
}

// ...use the DecorateAll decorator
@DecorateAll(Uppercase)
class Hello2 {
    world() {
        return 'world';
    }
    galaxy() {
        return 'galaxy';
    }
}

const hello1 = new Hello1();
console.log(hello1.world()); // logs "WORLD"
console.log(hello1.galaxy()); // logs "GALAXY"

const hello2 = new Hello2();
console.log(hello2.world()); // logs "WORLD"
console.log(hello2.galaxy()); // logs "GALAXY"

Options

The second parameter of the DecorateAll decorator takes an options object with the following options:

  • exclude: string[]
    array of method names that won't be decorated
// apply the Uppercase decorator to all methods, except 'galaxy'
@DecorateAll(Uppercase, { exclude: ['galaxy'] })
class Hello {
    world() {
        return 'world';
    }
    galaxy() {
        return 'galaxy';
    }
    universe() {
        return 'universe';
    }
}

const hello = new Hello();
console.log(hello.world()); // logs "WORLD"
console.log(hello.galaxy()); // logs "galaxy"
console.log(hello.universe()); // logs "UNIVERSE"
  • excludePrefix: string
    methods with the given prefix won't be decorated
// apply the Uppercase decorator to all methods, except 'galaxy'
@DecorateAll(Uppercase, { excludePrefix: '_' })
class Hello {
    world() {
        return 'world';
    }
    _galaxy() {
        return 'galaxy';
    }
    universe() {
        return 'universe';
    }
}

const hello = new Hello();
console.log(hello.world()); // logs "WORLD"
console.log(hello._galaxy()); // logs "galaxy"
console.log(hello.universe()); // logs "UNIVERSE"
  • deep: boolean
    By default, only the class' own methods are decorated. If you pass deep: true, the decorator will be also applied to inherited methods of any extended class. (It can also be combined with the exclude options).
class Plain {
    hi() {
        return 'hi';
    }
    hello() {
        return 'hello';
    }
}

@DecorateAll(Uppercase, { deep: true })
class Decorated extends Plain {}

const plain = new Plain();
console.log(plain.hi()); // logs "hi"
console.log(plain.hello()); // logs "hello"

const decorated = new Decorated();
console.log(decorated.hi()); // logs "HI"
console.log(decorated.hello()); // logs "HELLO"

decorate-all's People

Contributors

papooch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

decorate-all's Issues

Types of parameters 'descriptor' and 'descriptor' are incompatible.

Hello,

When trying to use @DecorateAll(ApiParam({/* ... */])) (from @nestjs/swagger), TypeScript complains with the following error :

Argument of type 'MethodDecorator' is not assignable to parameter of type '(target: any, propertyKey: string, descriptor?: PropertyDescriptor | undefined) => void'.
  Types of parameters 'descriptor' and 'descriptor' are incompatible.
    Type 'PropertyDescriptor | undefined' is not assignable to type 'TypedPropertyDescriptor<any>'.
      Type 'undefined' is not assignable to type 'TypedPropertyDescriptor<any>'.

Shouldn't the decorator argument (of DecorateAll()) below simply be of type MethodDecorator?

decorator: (
target: any,
propertyKey: string,
descriptor?: PropertyDescriptor,
) => void,

Allow decorating properties in addition to methods

I've liked your package and based on my needs I needed a function that apply decorators to properties instead of methods, so I got the inspiration from your package and created a little set of functionalities to do the job , I haven't used them yet nor properly tested them yet , I would like you to take a look on them as I'm a beginner thanks

const enum decoratingOptions {
  methods,
  members,
  all,
}

type decoratorType = (
  target: any,
  propertyKey: string,
  descriptor?: PropertyDescriptor
) => void;
type excludes = string[] | string;
type optionsType = { exclude?: excludes; deep?: boolean };

const checkOptions = (excludes: excludes, propName: string) => {
  if (Array.isArray(excludes)) {
    return excludes.includes(propName);
  } else return excludes === propName;
};
function applyDecorator(
  decorator: decoratorType,
  target: any,
  descriptors: {
    [x: string]: TypedPropertyDescriptor<any>;
  } & {
    [x: string]: PropertyDescriptor;
  },
  decoratingOptions,
  exclude
) {
  for (const [propName, descriptor] of Object.entries(descriptors)) {
const isMethod =typeof descriptor.value == "function" && propName != "constructor";
    const isMember=typeof descriptor.value != "function" 
    if (checkOptions(exclude, propName)) continue;
    if (decoratingOptions.methods) {
      if (!isMethod) continue;
    } else if (decoratingOptions.members) {
      if (!isMember) continue;
    }
    decorator(target, propName, descriptor);
    Object.defineProperty(target.prototype, propName, descriptor);
  }
}
function getDescriptors(target, deep: boolean) {
  let descriptors = Object.getOwnPropertyDescriptors(target.prototype);
  if (deep) {
    let base = Object.getPrototypeOf(target);
    while (base.prototype) {
      const baseDescriptors = Object.getOwnPropertyDescriptors(base.prototype);
      descriptors = { ...baseDescriptors, ...descriptors };
      base = Object.getPrototypeOf(base);
    }
  }
  return descriptors;
}

export function DecorateMembers(
  decorators: decoratorType[] | decoratorType,
  options: optionsType = {}
) {
  return Decorate(decorators, decoratingOptions.members, options);
}

export function DecorateMethods(
  decorators: decoratorType[] | decoratorType,
  options: optionsType = {}
) {
  return Decorate(decorators, decoratingOptions.methods, options);
}

export const Decorate = (
  decorators: decoratorType[] | decoratorType,
  decoratingOption: decoratingOptions,
  options: optionsType = {}
) => {
  if (Array.isArray(decorators)) {
    return (target: any) => {
      let descriptors = getDescriptors(target, options.deep);
      for (const decorator of decorators) {
        applyDecorator(
          decorator,
          target,
          descriptors,
          decoratingOption,
          options.exclude
        );
      }
    };
  } else {
    return (target: any) => {
      let descriptors = getDescriptors(target, options.deep);
      applyDecorator(
        decorators,
        target,
        descriptors,
        decoratingOption,
        options.exclude
      );
    };
  }
};

Option to exclude method with certain prefix

This would allow to easily exclude "private" methods that begin with a certain prefix, commonly an underscore _.

@DecorateAll(MyDecorator, { excludePrefix: '_' })
class MyClass {
   private _privateMethod() {} // will not be decorated
   publicMethod() {} // will be decorated
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.