lodash-decorators
ES7 Decorators for lodash functions.
Install
npm install --save lodash-decorators
Usage
For more in depth documentation please visit Lodash
With Arguments
Many of the lodash decorators can contain arguments.
debounce
throttle
memoize
after
before
ary
curry
curryRight
restParam
partial
partialRight
wrap
compose
flow
flowRight
backflow
delay
defer
bind
bindAll
modArgs
Example
import { after, debounce, memoize, curry } from 'lodash-decorators'
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@after(3)
@debounce(100)
getFullName() {
return `${this.firstName} ${this.lastName}`
}
@curry(2)
@memoize()
doSomeHeavyProcessing(arg1, arg2) {
}
}
Without Arguments
Some decorators don't take any arguments at all.
Example
import { once } from 'lodash-decorators'
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@once
getFullName() {
return `${this.firstName} ${this.lastName}`
}
@tap
popIt(list) {
list.pop();
}
}
const person = new Person();
person.popIt([1, 2, 3]);
Partials
Some decorators work slightly differently than you would expect
them to work than lodash.
These can take a Function
as their first argument or a String
.
If the argument is a String
then a Function
is resolved from
the current object.
Example
import { partial } from 'lodash-decorators'
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getName(type) {
return type === 'firstName' ? this.firstName : this.lastName
}
@partial('getName', 'firstName')
getFirstName() {}
@partial('getName', null)
getLastName() {}
@wrap('getName')
getUpperCaseName(fn) {
return fn().toUpperCase();
}
}
const person = new Person('Joe', 'Smith');
person.getFirstName();
person.getLastName();
person.getUpperCaseName();
Composition
You can use methods like compose
and flow
similiar to
partials. The arguments are resolved the same way partials
are resolved.
Example
import { compose } from 'lodash-decorators'
import { kebabCase } from 'lodash';
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getName() {
return `${this.firstName} ${this.lastName}`;
}
@compose(kebabCase, 'getName')
logName(name) {
console.log(name);
}
}
const person = new Person('Joe', 'Smith');
person.logName();
Instance Decorators
Normally decorators are applied to the prototype method
of the class you are working with, but with some of these
decorators that is not the desired behavour. These
decorators are applied at the instance level.
debounce
throttle
memoize
after
before
Note: Due to the nature of how instance decorators work they MUST be processed
after all prototype decorators in the decorator chain. There isn't a graceful way
to get around this currently. More or less instance decorators are kind of a hack and experimental.
class Person {
@curry(2)
@debounce(100)
getName() {}
@debounce(100)
@curry(2)
getName2() {}
}
Getters and Setters
Most decorators can be applied directly to getter and setter methods.
Example
import { once, compose } from 'lodash-decorators'
import _ from 'lodash';
function alwaysArray(value) {
return Array.isArray(value) ? value : _.isUndefined(value) ? [] : [value];
}
class Person {
constructor() {}
@once.get
get names() {
return this.nameList.join(' ');
}
@compose.set(alwaysArray)
set names(names) {
this.nameList = names;
}
}
const person = new Person();
person.names = undefined;
person.names = 'Joe';
person.names = ['Jim'];
What's with the .get
?
The decorator has no way to tell whether you are applying the decorator to the getter or setter (when both are provided).
The decorator just receives the descriptor which has both values provided and no way to distinguish which one you are provided
which decorator to.
@once.get
uses a form of the decorator that explicitly applies to the getter method.
@once.set
uses a form of the decorator that explicitly applies to the setter method.
Use at you're own risk...
Bind
Bind takes arguments based on lodash's bind and binds the Function
to
the current instance object.
Known Issue: When using bind on a single method the bind decorator MUST come last
in the chain of decorators. There is no graceful solution for this currently. You can always
use @bindAll('fn')
on the class and only include the functions you want to include.
Example
import { bind } from 'lodash-decorators'
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@bind()
getName() {
return `${this.firstName} ${this.lastName}`;
}
@bind('Joe')
getUpperCaseName(name) {
return name.toUpperCase();
}
}
const person = new Person('Joe', 'Smith');
person.getName.call(null);
person.getUpperCaseName();
You can also bind entire classes with bindAll
or bind
.
Note: Using @bind()
on a class delegates to the @bindAll()
implemenation.
Example
import { bind } from 'lodash-decorators'
@bindAll()
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getName() {
return `${this.firstName} ${this.lastName}`;
}
}
const person = new Person('Joe', 'Smith');
person.getName.call(null);
Forcing Decorator on Prototype
You can force an instance decorator to apply to the prototype instead of the instance.
Example
import { throttle } from 'lodash-decorators';
class Person {
@throttle(1000)
doStuff() {}
@throttle.proto(1000)
doStuffMore() {}
}
const person = new Person();
const person2 = new Person();
person.doStuff();
person2.doStuff();
person.doStuffMore();
person2.doStuffMore();
Extensions
Extensions are decorators that aren't necessarily Lodash functions, but use Lodash under the hood. They
provided some more basic utilities not found in Lodash;
deprecated
writable
configurable
returnsArg
enumerable
nonenumerable
-> enumerable(false)
nonconfigurable
-> configurable(false)
readonly
-> writable(false)
Deprecated
Warns when a deprecated class is istantiated or a deprecated class method is invoked.
You can also modify the deprecated behaviour by swapping out the method and class actions.
Example
import { deprecated } from 'lodash-decorators/extensions'
deprecated.methodAction = fn => console.log(`Don't use ${fn.name}!`);
@deprecated
class Person {
constructor() {}
}
class OtherPerson {
@deprecated
fn() {}
}
let person = new Person();
let otherPerson = new OtherPerson();
otherPerson.fn();
Validate
The validate module contains decorators that can validate function arguments and return value.
These can be found in src/validate
Author: Steven Sojka
MIT Licensed