Skip to content
This repository has been archived by the owner on Sep 29, 2020. It is now read-only.

Updated for full TypeScript Support #154

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -15,14 +15,18 @@ npm install core-decorators --save

This can be consumed by any transpiler that supports stage-0 of the decorators spec, like [babel.js](https://babeljs.io/) version 5. *Babel 6 [does not yet support decorators natively](https://phabricator.babeljs.io/T2645), but you can include [babel-plugin-transform-decorators-legacy](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy) or use the [`applyDecorators()` helper](#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!

##### Bower/globals

A globals version is available [here in the artifact repo](https://github.com/jayphelps/core-decorators-artifacts), 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](https://github.com/bower/bower/pull/1748) and IMO for very good reasons.

### TypeScript Support

Full TypeScript support has been added. Although there are differences in the way compilers like Babel and TypeScript compile output, this library has been updated to work _almost_ uniformly across both (see caveat in [@lazyInitialize](#lazyinitalize)). It should be noted, though, that because of the experimental nature of the features we are working with, compiler functionality may change in future versions.

Everything seems to work as of TypeScript 3.5.1 & ts-node 8.2.0. If you are concerned, try cloning the repo and running tests with your desired compiler version.

## Need lodash utilities as decorators?

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](https://www.npmjs.com/package/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.
@@ -357,7 +361,7 @@ count === 1;
// true
```

### @lazyInitialize
### @lazyInitialize

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.

@@ -385,6 +389,28 @@ editor.hugeBuffer;
// createHugeBuffer() is not called again
```

#### TypeScript Caveat
Babel's compiler supports 'initalizer' in property descriptor, which allows for the above syntax to work. If using TypeScript / non-babel compiler, you can accomplish the same by passing an initializer function to the decorator as a parameter.

The first argument is the function, all further arguments will be passed to that initializer function.

```typescript
import { lazyInitialize } from 'core-decorators';

const createHugeBuffer = () => new Array(1000000);

const addNumbers = (...numbers) => numbers.reduce((p,c)=>p+c);

class Editor {
@lazyInitialize(createHugeBuffer)
hugeBuffer:ArrayBuffer;

@lazyInitialize(addNumbers,2,5,8) // = 15
myCount:number;
}
```


### @mixin (alias: @mixins) :no_entry_sign: DEPRECATED

Mixes in all property descriptors from the provided Plain Old JavaScript Objects (aka POJOs) as arguments. Mixins are applied in the order they are passed, but do **not** override descriptors already on the class, including those inherited traditionally.
194 changes: 172 additions & 22 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,219 @@
// Type definitions for core-decorators.js 0.19
// Type definitions for core-decorators.js 0.21.0
// Project: https://github.com/jayphelps/core-decorators.js
// Definitions by: Qubo <https://github.com/tkqubo>
// dtweedle <https://github.com/dtweedle>
// TypeScript Version: 2.4.1
// Definitions by: Qubo <https://github.com/tkqubo>
// dtweedle <https://github.com/dtweedle>
// Ron Spickenagel <https://github.com/ronspickenagel>
// TypeScript Version: 3.5.1

export interface PropertyOrMethodDecorator extends MethodDecorator, PropertyDecorator {
(target: object, propertyKey: string | symbol): void;
export interface PropertyOrMethodDecorator {
(target: Object, propertyKey: PropertyKey, descriptor?: PropertyDescriptor): void
}

export interface Deprecate extends MethodDecorator {
(message?: string, option?: DeprecateOption): MethodDecorator;
export interface Type<T> extends Function {
new (...args: any[]): T;
}

export interface DeprecateOption {
url: string;
}

/**
* 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;
*/
export const autobind: Function;
export function autobind <TFunction extends Function>(target: TFunction): TFunction | void;
/**
* 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;
*/
export function autobind <T>(target: Object, propertyKey: PropertyKey, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;

/**
* Marks a property or method as not being writable.
*/
export const readonly: PropertyOrMethodDecorator;


/**
* Checks that the marked method indeed overrides a function with the same signature somewhere on the prototype chain.
*/
export const override: MethodDecorator;


/**
* 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.
* Calls console.warn() with a deprecation message. You can also provide an options hash with a url, for further reading.
* @param msg - Custom message to override the default one.
* @param options.url - URL for further detail
*/
export const deprecate: Deprecate;
export function deprecate (msg?: string, options?: { url?: string }): PropertyOrMethodDecorator
/**
* 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.
* Calls console.warn() with a deprecation message. You can also provide an options hash with a url, for further reading.
* @param msg - Custom message to override the default one.
* @param options.url - URL for further detail
*/
export const deprecated: Deprecate;
export function deprecate <T>(target: Object, propertyKey: PropertyKey, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;
/**
* Calls console.warn() with a deprecation message. You can also provide an options hash with a url, for further reading.
* @param msg - Custom message to override the default one.
* @param options.url - URL for further detail
*/
export function deprecated (msg?: string, options?: { url?: string }): PropertyOrMethodDecorator
/**
* Calls console.warn() with a deprecation message. You can also provide an options hash with a url, for further reading.
* @param msg - Custom message to override the default one.
* @param options.url - URL for further detail
*/
export function deprecated <T>(target: Object, propertyKey: PropertyKey, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;


/**
* Suppresses any JavaScript console.warn() call while the decorated function is called. (i.e. on the stack)
*/
export const suppressWarnings: MethodDecorator;


/**
* Marks a property or method as not being enumerable.
*/
export const nonenumerable: PropertyOrMethodDecorator;


/**
* Marks a property or method as not being writable.
*/
export const nonconfigurable: PropertyOrMethodDecorator;


/**
* Initial implementation included, likely slow. WIP.
* (Deprecated) Initial implementation included, likely slow. WIP.
*/
export const memoize: MethodDecorator;


/**
* 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.
* @param decorator - Decorator to apply
* @param args - Argument to pass to decorator
*/
export function decorate (decorator: PropertyOrMethodDecorator, ...args:any[]): PropertyOrMethodDecorator;


/**
* Prevents a property initializer from running until the decorated property is actually looked up.
* _Note: If you're not using Babel, you must pass initilizer function as a decorator parameter or it will not work._
*
* Usage:
* Babel - @lazyInitialize myParam = myInitializer()
* TypeScript - @lazyInitialize(myInitializer) myParam:myType
* @param initializer - Value initializer method
* @param args - Arguments to pass to initializer
* @see <a href="https://www.npmjs.com/package/core-decorators#lazyinitialize">lazyInitialize Caveats</a>
*/
export function decorate(func: Function, ...args: any[]): MethodDecorator;
export function lazyInitialize(initializer?: Function, args?: any[]);
/**
* 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.
* _Note: If you're not using Babel, you must pass initilizer function as a decorator parameter or it will not work._
*
* Usage:
* Babel - @lazyInitialize myParam = myInitializer()
* TypeScript - @lazyInitialize(myInitializer) myParam:myType
* @param initializer - Value initializer method
* @param args - Arguments to pass to initializer
* @see <a href="https://www.npmjs.com/package/core-decorators#lazyinitialize">lazyInitialize Caveats</a>
*/
export const lazyInitialize: PropertyDecorator;
export function lazyInitialize (target: Object, propertyKey: PropertyKey): void;


/**
* Uses console.time and console.timeEnd to provide function timings with a unique label whose default prefix is ClassName.method. Supply a first argument to override the prefix:
* @param label - Override the preifx
* @param Console - Custom console object
*/
export function time (label?: string, console?: Console): MethodDecorator;
/**
* Uses console.time and console.timeEnd to provide function timings with a unique label whose default prefix is ClassName.method. Supply a first argument to override the prefix:
* @param label - Override the preifx
* @param Console - Custom console object
*/
export function time <T>(target: Object, propertyKey: PropertyKey, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;


/**
* Marks a property or method as being enumerable
*/
export const enumerable: PropertyOrMethodDecorator;


/**
* (Deprecated) Creates a new debounced function which will be invoked after wait milliseconds since the time it was invoked. Default timeout is 300 ms.
* @param wait - Wait time in ms
* @param triggerOnLeadingq - Trigger function on the leading instead of the trailing edge of the wait interval
*/
export function debounce (wait: number, triggerOnLeading?: boolean): MethodDecorator;
/**
* (Deprecated) Creates a new debounced function which will be invoked after wait milliseconds since the time it was invoked. Default timeout is 300 ms.
* @param wait - Wait time in ms
* @param triggerOnLeadingq - Trigger function on the leading instead of the trailing edge of the wait interval
*/
export function debounce <T>(target: Object, propertyKey: PropertyKey, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void


/**
* (Deprecated) Creates a new throttled function which will be invoked in every <wait> milliseconds. Default timeout is 300 ms.
* @param wait [default=300] - Interval in ms
* @param options.leading [default=true] - Trigger function on the leading.
* @param options.trailing [default=true] - Trigger function on the trailing edge of the wait interval.
*/
export function throttle (wait?: number, options?: {leading?: boolean, trailing?: boolean} ): MethodDecorator;
/**
* (Deprecated) Creates a new throttled function which will be invoked in every <wait> milliseconds. Default timeout is 300 ms.
* @param wait - Interval in ms
* @param options.leading - Trigger function on the leading.
* @param options.trailing - Trigger function on the trailing edge of the wait interval.
*/
export function throttle <T>(target: Object, propertyKey: PropertyKey, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void


/**
* (Deprecated) Mixes in all property descriptors from the provided Plain Old JavaScript Objects (aka POJOs) as arguments.
* Mixins are applied in the order they are passed, but do not override descriptors already on the class, including those inherited traditionally.
*/
export function mixin (...mixins: object[]): ClassDecorator;
/**
* (Deprecated) Mixes in all property descriptors from the provided Plain Old JavaScript Objects (aka POJOs) as arguments.
* Mixins are applied in the order they are passed, but do not override descriptors already on the class, including those inherited traditionally.
*/
export function mixins (...mixins: object[]): ClassDecorator;


/**
* Extends the new property descriptor with the descriptor from the super/parent class prototype.
*/
export const extendDescriptor: PropertyOrMethodDecorator;


/**
* Uses console.profile and console.profileEnd to provide function profiling with a unique label whose default prefix is ClassName.method
* @param prefix - Override the prefix
* @param runOnce - Because profiling is expensive, set to true to prevent running every time the function is called.
*/
export function profile (prefix?: string | null, runOnce?: boolean): MethodDecorator;
/**
* Uses console.profile and console.profileEnd to provide function profiling with a unique label whose default prefix is ClassName.method
* @param prefix - Override the prefix
* @param msBetweenProfiles - Delay in milliseconds between profiles. Profiling is always ran on the leading edge.
*/
export function profile (prefix?: string | null, msBetweenProfiles?: number): MethodDecorator;
/**
* Uses console.profile and console.profileEnd to provide function profiling with a unique label whose default prefix is ClassName.method
* @param prefix - Override the prefix
* @param shouldProfileCallback - A function that returns a boolean to determine if profiling should occur. The function will have this context of the instance and the arguments to the method will be passed to the function as well. Arrow functions will not receive the instance context.
*/
export function profile (prefix?: string | null, shouldProfileCallback?: (...args:any[]) => boolean): MethodDecorator;
/**
* Uses console.profile and console.profileEnd to provide function profiling with a unique label whose default prefix is ClassName.method
*/
export function profile <T>(target: Object, propertyKey: PropertyKey, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;


/**
* The applyDecorators() helper can be used when you don't have language support for decorators like in Babel 6 or even with vanilla ES5 code without a transpiler.
*/
export function time(label: string, console?: Console): MethodDecorator;
export const applyDecorators: (Class: Type<any> | Function, props: Record<string,{[i:string]: PropertyOrMethodDecorator}>) => any;
20 changes: 12 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "core-decorators",
"version": "0.20.0",
"version": "0.21.0",
"description": "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! Works great with React/Angular/more!",
"main": "lib/core-decorators.js",
"module": "es/core-decorators.js",
@@ -21,7 +21,9 @@
"prebuild-tsc": "rimraf built",
"build-tsc": "tsc --outDir lib",
"clean": "rimraf lib es built",
"test": "mocha --compilers js:babel-core/register --require babel-polyfill \"test/**/*.spec.js\""
"test:babel": "cross-env TEST_MODE=babel mocha --require babel-core/register --require babel-polyfill \"test/**/*.spec.js\"",
"test:ts": "cross-env TEST_MODE=typescript mocha --require ts-node/register \"test/**/*.spec.js\"",
"test": "npm run test:babel && npm run test:ts"
},
"repository": {
"type": "git",
@@ -55,6 +57,7 @@
"@types/chai": "^3.5.2",
"@types/mocha": "^2.2.41",
"@types/node": "^6.0.73",
"@types/sinon": "^7.0.12",
"babel-cli": "^6.24.0",
"babel-core": "^6.24.0",
"babel-plugin-transform-class-properties": "^6.23.0",
@@ -66,15 +69,16 @@
"babel-preset-es2015": "^6.24.0",
"camelcase": "^4.1.0",
"chai": "^3.5.0",
"cross-env": "^5.0.0",
"cross-env": "^5.2.0",
"glob": "^7.1.1",
"global-wrap": "^2.0.0",
"interop-require": "1.0.0",
"lodash": "4.17.5",
"mocha": "^3.2.0",
"lodash": "^4.17.11",
"mocha": "^6.1.4",
"rimraf": "^2.6.1",
"sinon": "2.1.0",
"sinon-chai": "^2.9.0",
"typescript": "^2.3.3"
"sinon": "7.3.2",
"sinon-chai": "^3.3.0",
"ts-node": "^8.2.0",
"typescript": "^3.5.1"
}
}
3 changes: 2 additions & 1 deletion src/debounce.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { decorate, metaFor, internalDeprecation } from './private/utils';
import { decorate, internalDeprecation } from './private/utils';
import {metaFor} from "./private/meta";

const DEFAULT_TIMEOUT = 300;

Loading