From ec5b801616828a56e3de315675d7b525b6953db6 Mon Sep 17 00:00:00 2001 From: tkqubo Date: Sat, 3 Oct 2015 23:28:25 +0900 Subject: [PATCH] Add lodash-decorators --- lodash-decorators/lodash-decorators-tests.ts | 281 +++++++++++++++++++ lodash-decorators/lodash-decorators.d.ts | 141 ++++++++++ 2 files changed, 422 insertions(+) create mode 100644 lodash-decorators/lodash-decorators-tests.ts create mode 100644 lodash-decorators/lodash-decorators.d.ts diff --git a/lodash-decorators/lodash-decorators-tests.ts b/lodash-decorators/lodash-decorators-tests.ts new file mode 100644 index 000000000..3ccf0e211 --- /dev/null +++ b/lodash-decorators/lodash-decorators-tests.ts @@ -0,0 +1,281 @@ +/// +/// + +// +// 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: Function) { + return fn().toUpperCase(); + } +} + +const person3 = new Person3('Joe', 'Smith'); + +person3.getFirstName(); // 'Joe' +person3.getLastName(); // 'Smith' +//FIXME: method signature changed by lodash-decorators +(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); + } +} + +const person4 = new Person4('Joe', 'Smith'); + +//FIXME: method signature changed by lodash-decorators +(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[]; + + @once.get + get names(): string[] { + //FIXME: Resolve type inconsistency + return [this.nameList.join(' ')]; + //MEMO: Original expression in repo + //return this.nameList.join(' '); + } + + //TODO: So far, TypeScript doesn't allow to put a decorator here + // see https://github.com/Microsoft/TypeScript/issues/2249#issuecomment-141684146 + //@compose.set(alwaysArray) + set names(names: string[]) { + this.nameList = names; + } +} + +const person6 = new Person6(); + +// nameList will always be an array. +person6.names = undefined; //=> [] +//FIXME: method signature changed by lodash-decorators +(person6).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(); + } +} + +const person7 = new Person7('Joe', 'Smith'); + +person7.getName.call(null); // Joe Smith +//FIXME: method signature changed by lodash-decorators +(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! + diff --git a/lodash-decorators/lodash-decorators.d.ts b/lodash-decorators/lodash-decorators.d.ts new file mode 100644 index 000000000..56b1bd8b7 --- /dev/null +++ b/lodash-decorators/lodash-decorators.d.ts @@ -0,0 +1,141 @@ +// Type definitions for lodash-decorators 1.0.5 +// Project: https://github.com/steelsojka/lodash-decorators +// Definitions by: Qubo +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/// + + +declare module "lodash-decorators" { + // Originally copied from ../node_modules/typescript/lib/lib.es6.d.ts + export interface ClassDecorator { + (target: TFunction): TFunction|void; + } + export interface PropertyDecorator { + (target: Object, propertyKey: string | symbol): void; + } + export interface MethodDecorator { + (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor | void; + } + export interface ParameterDecorator { + (target: Object, propertyKey: string | symbol, parameterIndex: number): void; + } + + export interface MethodDecoratorWithAccessor extends MethodDecorator, Accessor { + } + + export interface Accessor { + 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 { + (...args: any[]): MethodDecorator; + } + export interface BindAllDecorator { + (...methodNames: string[]): ClassDecorator; + } + export interface ModArgsDecorator { + (...transforms: Function[]): MethodDecorator; + } + + export const debounce: DebounceDecorator & Accessor; + export const throttle: ThrottleDecorator & Accessor; + export const memoize: MemoizeDecorator & Accessor; + export const after: AfterDecorator & Accessor; + export const before: BeforeDecorator & Accessor; + export const ary: AryDecorator & Accessor; + export const curry: CurryDecorator & Accessor; + export const curryRight: CurryRightDecorator & Accessor; + export const restParam: RestParamDecorator & Accessor; + export const partial: PartialDecorator & Accessor; + export const partialRight: PartialDecorator & Accessor; + export const wrap: WrapDecorator & Accessor; + export const compose: ComposeDecorator & Accessor; + export const flow: ComposeDecorator & Accessor; + export const flowRight: ComposeDecorator & Accessor; + export const backflow: ComposeDecorator & Accessor; + export const delay: DelayDecorator & Accessor; + export const defer: DeferDecorator & Accessor; + export const bind: BindDecorator & Accessor; + export const bindAll: BindAllDecorator; + export const modArgs: ModArgsDecorator & Accessor; + export const once: MethodDecoratorWithAccessor; + export const spread: MethodDecoratorWithAccessor; + export const rearg: MethodDecoratorWithAccessor; + export const negate: MethodDecoratorWithAccessor; + export const tap: MethodDecoratorWithAccessor; +} + +declare module "lodash-decorators/extensions" { + // Originally copied from ../node_modules/typescript/lib/lib.es6.d.ts + export interface ClassDecorator { + (target: TFunction): TFunction|void; + } + export interface PropertyDecorator { + (target: Object, propertyKey: string | symbol): void; + } + export interface MethodDecorator { + (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor | 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 writable: (writable?: boolean) => MethodDecorator; + export const Writable: (writable?: boolean) => MethodDecorator; + export const configurable: (configurable?: boolean) => MethodDecorator; + export const returnsArg: (index?: number) => MethodDecorator; + export const enumerable: (enumerable?: boolean) => MethodDecorator; + export const nonenumerable: MethodDecorator; + export const nonconfigurable: MethodDecorator; + export const readonly: MethodDecorator; +}