diff --git a/lodash-decorators/lodash-decorators-tests.ts b/lodash-decorators/lodash-decorators-tests.ts new file mode 100644 index 000000000..60a659c8d --- /dev/null +++ b/lodash-decorators/lodash-decorators-tests.ts @@ -0,0 +1,370 @@ +/// +/// + +// +// 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([c => c > 0]) + mul(a: number, b: number): number { + return a + b; + } +} diff --git a/lodash-decorators/lodash-decorators-tests.ts.tscparams b/lodash-decorators/lodash-decorators-tests.ts.tscparams new file mode 100644 index 000000000..3f0863ac6 --- /dev/null +++ b/lodash-decorators/lodash-decorators-tests.ts.tscparams @@ -0,0 +1 @@ +--experimentalDecorators --noImplicitAny --target ES5 diff --git a/lodash-decorators/lodash-decorators.d.ts b/lodash-decorators/lodash-decorators.d.ts new file mode 100644 index 000000000..ac01c5cf6 --- /dev/null +++ b/lodash-decorators/lodash-decorators.d.ts @@ -0,0 +1,272 @@ +// 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 TypedMethodDecorator { + (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor | 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 { + (): TypedMethodDecorator<(() => R)>; + (param1?: T1): + TypedMethodDecorator<((param1: T1) => R)>; + (param1?: T1, param2?: T2): + TypedMethodDecorator<((param1: T1, param2: T2) => R)>; + (param1?: T1, param2?: T2, param3?: T3): + TypedMethodDecorator<((param1: T1, param2: T2, param3: T3) => R)>; + (param1?: T1, param2?: T2, param3?: T3, param4?: T4): + TypedMethodDecorator<((param1: T1, param2: T2, param3: T3, param4: T4) => R)>; + (param1?: T1, param2?: T2, param3?: T3, param4?: T4, param5?: T5): + TypedMethodDecorator<((param1: T1, param2: T2, param3: T3, param4: T4, param5: T5) => R)>; + (param1?: T1, param2?: T2, param3?: T3, param4?: T4, param5?: T5, param6?: T6): + TypedMethodDecorator<((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; + export const Debounce: DebounceDecorator & Accessor; + + export const throttle: ThrottleDecorator & Accessor; + export const Throttle: ThrottleDecorator & Accessor; + + export const memoize: MemoizeDecorator & Accessor; + export const Memoize: MemoizeDecorator & Accessor; + + export const after: AfterDecorator & Accessor; + export const After: AfterDecorator & Accessor; + + export const before: BeforeDecorator & Accessor; + export const Before: BeforeDecorator & Accessor; + + export const ary: AryDecorator & Accessor; + export const Ary: AryDecorator & Accessor; + + export const curry: CurryDecorator & Accessor; + export const Curry: CurryDecorator & Accessor; + + export const curryRight: CurryRightDecorator & Accessor; + export const CurryRight: CurryRightDecorator & Accessor; + + export const restParam: RestParamDecorator & Accessor; + export const RestParam: RestParamDecorator & Accessor; + + export const partial: PartialDecorator & Accessor; + export const Partial: PartialDecorator & Accessor; + + export const partialRight: PartialDecorator & Accessor; + export const PartialRight: PartialDecorator & Accessor; + + export const wrap: WrapDecorator & Accessor; + export const Wrap: WrapDecorator & Accessor; + + export const compose: ComposeDecorator & Accessor; + export const Compose: ComposeDecorator & Accessor; + + export const flow: ComposeDecorator & Accessor; + export const Flow: ComposeDecorator & Accessor; + + export const flowRight: ComposeDecorator & Accessor; + export const FlowRight: ComposeDecorator & Accessor; + + export const backflow: ComposeDecorator & Accessor; + export const Backflow: ComposeDecorator & Accessor; + + export const delay: DelayDecorator & Accessor; + export const Delay: DelayDecorator & Accessor; + + export const defer: DeferDecorator & Accessor; + export const Defer: DeferDecorator & Accessor; + + export const bind: BindDecorator & Accessor; + export const Bind: BindDecorator & Accessor; + + export const bindAll: BindAllDecorator; + export const BindAll: BindAllDecorator; + + export const modArgs: ModArgsDecorator & Accessor; + export const ModArgs: ModArgsDecorator & Accessor; + + 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 { + (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 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 { + (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 TypedMethodDecorator { + (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor | void; + } + + export interface Predicate { + (t: T): boolean; + } + type Predicates = Predicate|Predicate[]; + + export interface ValidateDecorator { + (p1: Predicates): + TypedMethodDecorator<((param1: T1) => R)>; + (p1: Predicates, p2?: Predicates): + TypedMethodDecorator<((param1: T1, param2: T2) => R)>; + (p1: Predicates, p2?: Predicates, p3?: Predicates): + TypedMethodDecorator<((param1: T1, param2: T2, param3: T3) => R)>; + (p1: Predicates, p2?: Predicates, p3?: Predicates, p4?: Predicates): + TypedMethodDecorator<((param1: T1, param2: T2, param3: T3, param4: T4) => R)>; + (p1: Predicates, p2?: Predicates, p3?: Predicates, p4?: Predicates, p5?: Predicates): + TypedMethodDecorator<((param1: T1, param2: T2, param3: T3, param4: T4, param5: T5) => R)>; + (p1: Predicates, p2?: Predicates, p3?: Predicates, p4?: Predicates, p5?: Predicates, p6?: Predicates): + TypedMethodDecorator<((param1: T1, param2: T2, param3: T3, param4: T4, param5: T5, param6: T6) => R)>; + } + + export interface ValidateReturnDecorator { + (p1: Predicates): TypedMethodDecorator<((...args: any[]) => R)>; + } + + export const validate: ValidateDecorator; + export const Validate: ValidateDecorator; + + export const validateReturn: ValidateReturnDecorator; + export const ValidateReturn: ValidateReturnDecorator; +}