Merge pull request #6113 from tkqubo/lodash-decorators

Lodash decorators
This commit is contained in:
Masahiro Wakame
2015-10-06 22:37:37 +09:00
3 changed files with 643 additions and 0 deletions
@@ -0,0 +1,370 @@
/// <reference path="lodash-decorators.d.ts" />
/// <reference path="../lodash/lodash.d.ts" />
//
// With Arguments
//
import { after, debounce, memoize, curry } from 'lodash-decorators'
class Person {
firstName: string;
lastName: string;
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
@after(3)
@debounce(100)
getFullName(): string {
return `${this.firstName} ${this.lastName}`
}
@curry(2)
@memoize()
doSomeHeavyProcessing(arg1: any, arg2: any) {
}
}
//
// Without Arguments
//
import { tap } from 'lodash-decorators'
class Person2 {
firstName: string;
lastName: string;
constructor(firstName?: string, lastName?: string) {
this.firstName = firstName;
this.lastName = lastName;
}
@once
getFullName(): string {
return `${this.firstName} ${this.lastName}`
}
@tap
popIt(list: number[]): void {
list.pop();
}
}
const person2 = new Person2();
person2.popIt([1, 2, 3]); //=> [1, 2]
//
// Partials
//
import { partial, wrap } from 'lodash-decorators'
class Person3 {
firstName: string;
lastName: string;
constructor(firstName?: string, lastName?: string) {
this.firstName = firstName;
this.lastName = lastName;
}
getName(type: string) {
return type === 'firstName' ? this.firstName : this.lastName
}
@partial('getName', 'firstName')
getFirstName(): string { return null; }
@partial('getName', null)
getLastName(): string { return null; }
@wrap('getName')
getUpperCaseName(fn: () => string): string {
return fn().toUpperCase();
}
}
//by .d.ts author: a workaround to ensure type
interface Person3Ex extends Person3 {
getUpperCaseName(): string;
}
const person3 = new Person3('Joe', 'Smith') as Person3Ex;
person3.getFirstName(); // 'Joe'
person3.getLastName(); // 'Smith'
person3.getUpperCaseName(); // JOE SMITH
//
// Composition
//
//import { kebabCase } from 'lodash';
import * as _ from 'lodash';
class Person4 {
firstName: string;
lastName: string;
constructor(firstName?: string, lastName?: string) {
this.firstName = firstName;
this.lastName = lastName;
}
getName(): string {
return `${this.firstName} ${this.lastName}`;
}
@compose(_.kebabCase, 'getName')
logName(name: string): void {
console.log(name);
}
}
//by .d.ts author: a workaround to ensure type
interface Person4Ex extends Person4 {
logName(): void;
}
const person4 = new Person4('Joe', 'Smith') as Person4Ex;
person4.logName(); // joe-smith
//
// Instance Decorators
//
class Person5 {
@curry(2) // <= prototype decorator
@debounce(100) // <= instance decorator
getName() {} //=> Throws an error. (╯°□°)╯︵ ┻━┻
@debounce(100) // <= instance decorator
@curry(2) // <= prototype decorator
getName2() {} //=> All is well :)
}
//
// Getters and Setters
//
import { once, compose } from 'lodash-decorators'
function alwaysArray(value: string|string[]): string[] {
return Array.isArray(value) ? value : _.isUndefined(value) ? [] : [value];
}
class Person6 {
constructor() {}
private nameList: string[];
//TODO: So far, TypeScript doesn't allow to put multiple decoratoes on set/get accessors.
// see https://github.com/Microsoft/TypeScript/issues/2249#issuecomment-141684146
//@once.get
get names(): string[] {
//MEMO: Resolve type inconsistency
return [this.nameList.join(' ')];
//MEMO: Original expression in repo
//return this.nameList.join(' ');
}
@compose.set(alwaysArray)
set names(names: string[]) {
this.nameList = names;
}
}
//by .d.ts author: a workaround to ensure type
interface Person6Alt {
names: string[]|string;
}
const person6 = new Person6();
// nameList will always be an array.
person6.names = undefined; //=> []
(person6 as Person6Alt).names = 'Joe'; //=> ['Joe']
person6.names = ['Jim']; //=> ['Jim']
//
// Bind
//
import { bind } from 'lodash-decorators'
class Person7 {
firstName: string;
lastName: string;
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
@bind()
getName(): string {
return `${this.firstName} ${this.lastName}`;
}
// It can also function as a partial
@bind('Joe')
getUpperCaseName(name: string): string {
return name.toUpperCase();
}
}
//by .d.ts author: a workaround to ensure type
interface Person7Ex extends Person7 {
getUpperCaseName(): string;
}
const person7 = new Person7('Joe', 'Smith') as Person7Ex;
person7.getName.call(null); // Joe Smith
person7.getUpperCaseName(); // JOE
import { bindAll } from 'lodash-decorators'
@bindAll()
class Person8 {
firstName: string;
lastName: string;
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
getName() {
return `${this.firstName} ${this.lastName}`;
}
}
const person8 = new Person8('Joe', 'Smith');
person8.getName.call(null); // Joe Smith
//
// Forcing Decorator on Prototype
//
import { throttle } from 'lodash-decorators';
class Person9 {
@throttle(1000)
doStuff() {}
@throttle.proto(1000)
doStuffMore() {}
}
const person9_1 = new Person9();
const person9_2 = new Person9();
person9_1.doStuff(); //=> Both are called
person9_2.doStuff();
person9_1.doStuffMore();
person9_2.doStuffMore();
// Only one of these methods is actual invoked because throttle is applied to the prototype method
// and not the instance method.
//
// Extensions
//
import { deprecated } from 'lodash-decorators/extensions'
// This is applied globally.
deprecated.methodAction = fn => console.log(`Don't use ${fn.name}!`);
@deprecated
class Person10 {
constructor() {}
}
class OtherPerson {
@deprecated
fn() {}
}
let person10 = new Person10(); //=> Warning!
let otherPerson = new OtherPerson();
otherPerson.fn(); //=> Don't use fn!
//
// https://github.com/steelsojka/lodash-decorators/tree/master/src/extensions
//
import { Writable, ReturnsArg } from 'lodash-decorators/extensions';
class Person11 {
constructor() {}
@Writable(false)
getName() {}
@ReturnsArg(1)
doSomething(x: any, y: any, z: any) {
// Do something here
}
}
//
// https://github.com/steelsojka/lodash-decorators/tree/master/src/validate
//
import { Validate } from 'lodash-decorators/validate';
class Person12 {
name: string;
constructor() {}
@Validate(_.isString)
setName(name: any) {
this.name = name as string;
}
}
class Person13 {
name: string;
age: number;
constructor() {}
@Validate(
_.isString,
[_.isNumber, _.partial(_.lt, 10) as ((_: any) => boolean)]
)
setData(name: any, age: any) {
this.name = name as string;
this.age = age as number;
}
}
const person13 = new Person13();
person13.setData('test', 5); //=> TypeError
person13.setData('test', 12); //=> Valid
//
// Additional typings
//
import { validateReturn } from 'lodash-decorators/validate';
class Calc {
@validateReturn((c: number) => c > 0)
add(a: number, b: number): number {
return a + b;
}
@validateReturn<number>([c => c > 0])
mul(a: number, b: number): number {
return a + b;
}
}
@@ -0,0 +1 @@
--experimentalDecorators --noImplicitAny --target ES5
+272
View File
@@ -0,0 +1,272 @@
// Type definitions for lodash-decorators 1.0.5
// Project: https://github.com/steelsojka/lodash-decorators
// Definitions by: Qubo <https://github.com/tkqubo>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
///<reference path="../lodash/lodash.d.ts"/>
declare module "lodash-decorators" {
// Originally copied from ../node_modules/typescript/lib/lib.es6.d.ts
export interface ClassDecorator {
<TFunction extends Function>(target: TFunction): TFunction|void;
}
export interface PropertyDecorator {
(target: Object, propertyKey: string | symbol): void;
}
export interface MethodDecorator {
<T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;
}
export interface ParameterDecorator {
(target: Object, propertyKey: string | symbol, parameterIndex: number): void;
}
export interface TypedMethodDecorator<TFunction extends Function> {
(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<TFunction>): TypedPropertyDescriptor<TFunction> | void;
}
export interface MethodDecoratorWithAccessor extends MethodDecorator, Accessor<MethodDecorator> {
}
export interface Accessor<T> {
set: T;
get: T;
proto: T;
}
export interface DebounceDecorator {
(wait: number, options?: _.DebounceSettings): MethodDecorator;
}
export interface ThrottleDecorator {
(wait: number, options?: _.ThrottleSettings): MethodDecorator;
}
export interface MemoizeDecorator {
(resolver?: Function): MethodDecorator;
}
export interface AfterDecorator {
(n: number): MethodDecorator;
}
export interface BeforeDecorator {
(n: number): MethodDecorator;
}
export interface AryDecorator {
(n: number): MethodDecorator;
}
export interface CurryDecorator {
(arity?: number): MethodDecorator;
}
export interface CurryRightDecorator {
(arity?: number): MethodDecorator;
}
export interface RestParamDecorator {
(start?: number): MethodDecorator;
}
export interface PartialDecorator {
(func: Function|string, ...args: any[]): MethodDecorator;
}
export interface WrapDecorator {
(wrapper: ((func: Function, ...args: any[]) => any)|string): MethodDecorator;
}
export interface ComposeDecorator {
(...funcs: (Function|string)[]): MethodDecorator;
}
export interface DelayDecorator {
(wait: number, ...args: any[]): MethodDecorator;
}
export interface DeferDecorator {
(...args: any[]): MethodDecorator;
}
export interface BindDecorator {
(): TypedMethodDecorator<(<R>() => R)>;
<T1>(param1?: T1):
TypedMethodDecorator<(<R>(param1: T1) => R)>;
<T1, T2>(param1?: T1, param2?: T2):
TypedMethodDecorator<(<R>(param1: T1, param2: T2) => R)>;
<T1, T2, T3>(param1?: T1, param2?: T2, param3?: T3):
TypedMethodDecorator<(<R>(param1: T1, param2: T2, param3: T3) => R)>;
<T1, T2, T3, T4>(param1?: T1, param2?: T2, param3?: T3, param4?: T4):
TypedMethodDecorator<(<R>(param1: T1, param2: T2, param3: T3, param4: T4) => R)>;
<T1, T2, T3, T4, T5>(param1?: T1, param2?: T2, param3?: T3, param4?: T4, param5?: T5):
TypedMethodDecorator<(<R>(param1: T1, param2: T2, param3: T3, param4: T4, param5: T5) => R)>;
<T1, T2, T3, T4, T5, T6>(param1?: T1, param2?: T2, param3?: T3, param4?: T4, param5?: T5, param6?: T6):
TypedMethodDecorator<(<R>(param1: T1, param2: T2, param3: T3, param4: T4, param5: T5, param6: T6) => R)>;
}
export interface BindAllDecorator {
(...methodNames: string[]): ClassDecorator;
}
export interface ModArgsDecorator {
(...transforms: Function[]): MethodDecorator;
}
export const debounce: DebounceDecorator & Accessor<DebounceDecorator>;
export const Debounce: DebounceDecorator & Accessor<DebounceDecorator>;
export const throttle: ThrottleDecorator & Accessor<ThrottleDecorator>;
export const Throttle: ThrottleDecorator & Accessor<ThrottleDecorator>;
export const memoize: MemoizeDecorator & Accessor<MemoizeDecorator>;
export const Memoize: MemoizeDecorator & Accessor<MemoizeDecorator>;
export const after: AfterDecorator & Accessor<AfterDecorator>;
export const After: AfterDecorator & Accessor<AfterDecorator>;
export const before: BeforeDecorator & Accessor<BeforeDecorator>;
export const Before: BeforeDecorator & Accessor<BeforeDecorator>;
export const ary: AryDecorator & Accessor<AryDecorator>;
export const Ary: AryDecorator & Accessor<AryDecorator>;
export const curry: CurryDecorator & Accessor<CurryDecorator>;
export const Curry: CurryDecorator & Accessor<CurryDecorator>;
export const curryRight: CurryRightDecorator & Accessor<CurryRightDecorator>;
export const CurryRight: CurryRightDecorator & Accessor<CurryRightDecorator>;
export const restParam: RestParamDecorator & Accessor<RestParamDecorator>;
export const RestParam: RestParamDecorator & Accessor<RestParamDecorator>;
export const partial: PartialDecorator & Accessor<PartialDecorator>;
export const Partial: PartialDecorator & Accessor<PartialDecorator>;
export const partialRight: PartialDecorator & Accessor<PartialDecorator>;
export const PartialRight: PartialDecorator & Accessor<PartialDecorator>;
export const wrap: WrapDecorator & Accessor<WrapDecorator>;
export const Wrap: WrapDecorator & Accessor<WrapDecorator>;
export const compose: ComposeDecorator & Accessor<ComposeDecorator>;
export const Compose: ComposeDecorator & Accessor<ComposeDecorator>;
export const flow: ComposeDecorator & Accessor<ComposeDecorator>;
export const Flow: ComposeDecorator & Accessor<ComposeDecorator>;
export const flowRight: ComposeDecorator & Accessor<ComposeDecorator>;
export const FlowRight: ComposeDecorator & Accessor<ComposeDecorator>;
export const backflow: ComposeDecorator & Accessor<ComposeDecorator>;
export const Backflow: ComposeDecorator & Accessor<ComposeDecorator>;
export const delay: DelayDecorator & Accessor<DelayDecorator>;
export const Delay: DelayDecorator & Accessor<DelayDecorator>;
export const defer: DeferDecorator & Accessor<DeferDecorator>;
export const Defer: DeferDecorator & Accessor<DeferDecorator>;
export const bind: BindDecorator & Accessor<BindDecorator>;
export const Bind: BindDecorator & Accessor<BindDecorator>;
export const bindAll: BindAllDecorator;
export const BindAll: BindAllDecorator;
export const modArgs: ModArgsDecorator & Accessor<ModArgsDecorator>;
export const ModArgs: ModArgsDecorator & Accessor<ModArgsDecorator>;
export const once: MethodDecoratorWithAccessor;
export const Once: MethodDecoratorWithAccessor;
export const spread: MethodDecoratorWithAccessor;
export const Spread: MethodDecoratorWithAccessor;
export const rearg: MethodDecoratorWithAccessor;
export const Rearg: MethodDecoratorWithAccessor;
export const negate: MethodDecoratorWithAccessor;
export const Negate: MethodDecoratorWithAccessor;
export const tap: MethodDecoratorWithAccessor;
export const Tap: MethodDecoratorWithAccessor;
}
declare module "lodash-decorators/extensions" {
// Originally copied from ../node_modules/typescript/lib/lib.es6.d.ts
export interface ClassDecorator {
<TFunction extends Function>(target: TFunction): TFunction|void;
}
export interface PropertyDecorator {
(target: Object, propertyKey: string | symbol): void;
}
export interface MethodDecorator {
<T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;
}
export interface ParameterDecorator {
(target: Object, propertyKey: string | symbol, parameterIndex: number): void;
}
export interface DeprecatedDecorator extends MethodDecorator, ClassDecorator {
methodAction(fn: Function & { name: string }): void;
}
export const deprecated: DeprecatedDecorator;
export const Deprecated: DeprecatedDecorator;
export const writable: (writable?: boolean) => MethodDecorator;
export const Writable: (writable?: boolean) => MethodDecorator;
export const configurable: (configurable?: boolean) => MethodDecorator;
export const Configurable: (configurable?: boolean) => MethodDecorator;
export const returnsArg: (index?: number) => MethodDecorator;
export const ReturnsArg: (index?: number) => MethodDecorator;
export const enumerable: (enumerable?: boolean) => MethodDecorator;
export const Enumerable: (enumerable?: boolean) => MethodDecorator;
export const nonenumerable: MethodDecorator;
export const Nonenumerable: MethodDecorator;
export const nonconfigurable: MethodDecorator;
export const Nonconfigurable: MethodDecorator;
export const readonly: MethodDecorator;
export const Readonly: MethodDecorator;
}
declare module "lodash-decorators/validate" {
// Originally copied from ../node_modules/typescript/lib/lib.es6.d.ts
export interface ClassDecorator {
<TFunction extends Function>(target: TFunction): TFunction|void;
}
export interface PropertyDecorator {
(target: Object, propertyKey: string | symbol): void;
}
export interface MethodDecorator {
<T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;
}
export interface ParameterDecorator {
(target: Object, propertyKey: string | symbol, parameterIndex: number): void;
}
export interface TypedMethodDecorator<TFunction extends Function> {
(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<TFunction>): TypedPropertyDescriptor<TFunction> | void;
}
export interface Predicate<T> {
(t: T): boolean;
}
type Predicates<T> = Predicate<T>|Predicate<T>[];
export interface ValidateDecorator {
<T1>(p1: Predicates<T1>):
TypedMethodDecorator<(<R>(param1: T1) => R)>;
<T1, T2>(p1: Predicates<T1>, p2?: Predicates<T2>):
TypedMethodDecorator<(<R>(param1: T1, param2: T2) => R)>;
<T1, T2, T3>(p1: Predicates<T1>, p2?: Predicates<T2>, p3?: Predicates<T3>):
TypedMethodDecorator<(<R>(param1: T1, param2: T2, param3: T3) => R)>;
<T1, T2, T3, T4>(p1: Predicates<T1>, p2?: Predicates<T2>, p3?: Predicates<T3>, p4?: Predicates<T4>):
TypedMethodDecorator<(<R>(param1: T1, param2: T2, param3: T3, param4: T4) => R)>;
<T1, T2, T3, T4, T5>(p1: Predicates<T1>, p2?: Predicates<T2>, p3?: Predicates<T3>, p4?: Predicates<T4>, p5?: Predicates<T5>):
TypedMethodDecorator<(<R>(param1: T1, param2: T2, param3: T3, param4: T4, param5: T5) => R)>;
<T1, T2, T3, T4, T5, T6>(p1: Predicates<T1>, p2?: Predicates<T2>, p3?: Predicates<T3>, p4?: Predicates<T4>, p5?: Predicates<T5>, p6?: Predicates<T6>):
TypedMethodDecorator<(<R>(param1: T1, param2: T2, param3: T3, param4: T4, param5: T5, param6: T6) => R)>;
}
export interface ValidateReturnDecorator {
<R>(p1: Predicates<R>): TypedMethodDecorator<((...args: any[]) => R)>;
}
export const validate: ValidateDecorator;
export const Validate: ValidateDecorator;
export const validateReturn: ValidateReturnDecorator;
export const ValidateReturn: ValidateReturnDecorator;
}