Library of stage-0 JavaScript decorators (aka ES2016/ES7 decorators but not accurate) inspired by languages that come with built-ins like @override, @deprecat
Library of stage-0 JavaScript decorators (aka ES2016/ES7 decorators but not accurate) inspired by languages that come with built-ins like @override, @deprecat
WARNING: this library was made using the JavaScript stage-0 decorators spec, not the latest version (stage-2) which drastically changed in breaking ways. As such, it was and still is highly experimental and at this point probably best avoided. If/when the decorators spec ever becomes stage-3, and at least one JS compiler supports it, this repo will be updated to support that specification and then we can work towards a stable v1.0.0 version. In the meantime, this repo should mostly considered unmaintained, except for any security/critical issues, as any work done would mostly be thrown away.
Library of JavaScript stage-0 decorators (aka ES2016/ES7 decorators but that's not accurate) inspired by languages that come with built-ins like @override, @deprecate, @autobind, @mixin and more. Popular with React/Angular, but is framework agnostic. Similar to Annotations in Java but unlike Java annotations, decorators are functions which are applied at runtime.
These are stage-0 decorators because while the decorators spec has changed and is now stage-2, no transpiler has yet to implement these changes and until they do, this library won't either. Although the TypeScript documentation uses the phrase "Decorators are a stage 2 proposal for JavaScript" this is misleading because TypeScript still only implements the stage-0 version of the spec, which is very incompatible with stage-2 (as of this writing). If you concretely find that a compiler (babel, TS, etc) implement stage-2+, please do link me to the appropriate release notes! :balloon:
*compiled code is intentionally not checked into this repo
A version compiled to ES5 in CJS format is published to npm as core-decorators
npm install core-decorators --save
This can be consumed by any transpiler that supports stage-0 of the decorators spec, like babel.js version 5. Babel 6 does not yet support decorators natively, but you can include babel-plugin-transform-decorators-legacy or use the applyDecorators() helper.
core-decorators does not officially support TypeScript. There are known incompatibilities with the way it transpiles the output. PRs certainly welcome to fix that!
A globals version is available here in the artifact repo, or via $ bower install core-decorators. It defines a global variable CoreDecorators, which can then be used as you might expect: @CoreDecorators.autobind(), etc.
I highly recommend against using that globals build as it's quite strange you're using decorators (a proposed future feature of JavaScript) while not using ES2015 modules, a spec ratified feature used by nearly every modern framework. Also--bower is on its deathbed and IMO for very good reasons.
core-decorators aims to provide decorators that are fundamental to JavaScript itself--mostly things you could do with normal Object.defineProperty but not as easily when using ES2015 classes. Things like debouncing, throttling, and other more opinionated decorators are being phased out in favor of lodash-decorators which wraps applicable lodash utilities as decorators. We don't want to duplicate the effort of lodash, which has years and years of robust testing and bugfixes.
Note: there is a bug in
react-hot-loader <= 1.3.0(they fixed in2.0.0-alpha-4) which prevents this from working as expected. Follow it here
Forces invocations of this function to always have this refer to the class instance, even if the function is passed around or would otherwise lose its this context. e.g. var fn = context.method; Popular with React components.
Individual methods:
import { autobind } from 'core-decorators';
class Person {
@autobind
getPerson() {
return this;
}
}
let person = new Person();
let { getPerson } = person;
getPerson() === person;
// true
Entire Class:
import { autobind } from 'core-decorators';
@autobind
class Person {
getPerson() {
return this;
}
getPersonAgain() {
return this;
}
}
let person = new Person();
let { getPerson, getPersonAgain } = person;
getPerson() === person;
// true
getPersonAgain() === person;
// true
Marks a property or method as not being writable.
import { readonly } from 'core-decorators';
class Meal {
@readonly
entree = 'steak';
}
var dinner = new Meal();
dinner.entree = 'salmon';
// Cannot assign to read only property 'entree' of [object Object]
Checks that the marked method indeed overrides a function with the same signature somewhere on the prototype chain.
Works with methods and getters/setters only (not property initializers/arrow functions). Will ensure name, parameter count, as well as descriptor type (accessor/data). Provides a suggestion if it finds a method with a similar signature, including slight misspellings.
import { override } from 'core-decorators';
class Parent {
speak(first, second) {}
}
class Child extends Parent {
@override
speak() {}
// SyntaxError: Child#speak() does not properly override Parent#speak(first, second)
}
// or
class Child extends Parent {
@override
speaks() {}
// SyntaxError: No descriptor matching Child#speaks() was found on the prototype chain.
//
// Did you mean "speak"?
}
Calls console.warn() with a deprecation message. Provide a custom message to override the default one. You can also provide an options hash with a url, for further reading.
…
Creates a new debounced function which will be invoked after wait milliseconds since the time it was invoked. Default timeout is 300 ms.
Optional boolean second argument allows to trigger function on the leading instead of the trailing edge of the wait interval. Implementation is inspired by similar method from UnderscoreJS.
import { debounce } from 'core-decorators';
class Editor {
content = '';
@debounce(500)
updateContent(content) {
this.content = content;
}
}
Creates a new throttled function which will be invoked in every wait milliseconds. Default timeout is 300 ms.
Second argument is optional options:
leading: default to true, allows to trigger function on the leading.trailing: default to true, allows to trigger function on the trailing edge of the wait interval.Implementation is inspired by similar method from UnderscoreJS.
import { throttle } from 'core-decorators';
class Editor {
content = '';
@throttle(500, {leading: false})
updateContent(content) {
this.content = content;
}
}
Suppresses any JavaScript console.warn() call while the decorated function is called. (i.e. on the stack)
Will not suppress warnings triggered in any async code within.
import { suppressWarnings } from 'core-decorators';
class Person {
@deprecated
facepalm() {}
@suppressWarnings
facepalmWithoutWarning() {
this.facepalm();
}
}
let person = new Person();
person.facepalmWithoutWarning();
// no warning is logged
Marks a method as being enumerable. Note that instance properties are already enumerable, so this is only useful for methods.
import { enumerable } from 'core-decorators';
class Meal {
pay() {}
@enumerable
eat() {}
}
var dinner = new Meal();
for (var key in dinner) {
key;
// "eat" only, not "pay"
}
Marks a property as not being enumerable. Note that class methods are already nonenumerable, so this is only useful for instance properties.
import { nonenumerable } from 'core-decorators';
class Meal {
entree = 'steak';
@nonenumerable
cost = 20.99;
}
var dinner = new Meal();
for (var key in dinner) {
key;
// "entree" only, not "cost"
}
Object.keys(dinner);
// ["entree"]
Marks a property or method so that it cannot be deleted; also prevents it from being reconfigured via Object.defineProperty, but this may not always work how you expect due to a quirk in JavaScript itself, not this library. Adding the @readonly decorator fixes it, but at the cost of obviously making the property readonly (aka writable: false). You can read more about this here.
import { nonconfigurable } from 'core-decorators';
class Foo {
@nonconfigurable
@readonly
bar() {};
}
Object.defineProperty(Foo.prototype, 'bar', {
value: 'I will error'
});
// Cannot redefine property: bar
Immediately applies the provided function and arguments to the method, allowing you to wrap methods with arbitrary helpers like those provided by lodash. The first argument is the function to apply, all further arguments will be passed to that decorating function.
import { decorate } from 'core-decorators';
import { memoize } from 'lodash';
var count = 0;
class Task {
@decorate(memoize)
doSomethingExpensive(data) {
count++;
// something expensive;
return data;
}
}
var task = new Task();
var data = [1, 2, 3];
task.doSomethingExpensive(data);
task.doSomethingExpensive(data);
count === 1;
// true
Prevents a property initializer from running until the decorated property is actually looked up. Useful to prevent excess allocations that might otherwise not be used, but be careful not to over-optimize things.
import { lazyInitialize } from 'core-decorators';
function createHugeBuffer() {
console.log('huge buffer created');
return new Array(1000000);
}
class Editor {
@lazyInitialize
hugeBuffer = createHugeBuffer();
}
var editor = new Editor();
// createHugeBuffer() has not been called yet
editor.hugeBuffer;
// logs 'huge buffer created', now it has been called
editor.hugeBuffer;
// already initialized and equals our buffer, so
// createHugeBuffer() is not called again
Mixes in all property descriptors from the provided Plai
No open issues yet, or sync has not completed.