From ec5b801616828a56e3de315675d7b525b6953db6 Mon Sep 17 00:00:00 2001 From: tkqubo Date: Sat, 3 Oct 2015 23:28:25 +0900 Subject: [PATCH 1/6] 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; +} From 26eb4a6e364d5630b24bee72502d0c6b87afb7a8 Mon Sep 17 00:00:00 2001 From: tkqubo Date: Sat, 3 Oct 2015 23:32:04 +0900 Subject: [PATCH 2/6] Add capitalized counterparts --- lodash-decorators/lodash-decorators-tests.ts | 19 ++++++ lodash-decorators/lodash-decorators.d.ts | 65 ++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/lodash-decorators/lodash-decorators-tests.ts b/lodash-decorators/lodash-decorators-tests.ts index 3ccf0e211..2b1cf8417 100644 --- a/lodash-decorators/lodash-decorators-tests.ts +++ b/lodash-decorators/lodash-decorators-tests.ts @@ -279,3 +279,22 @@ 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 + } +} + diff --git a/lodash-decorators/lodash-decorators.d.ts b/lodash-decorators/lodash-decorators.d.ts index 56b1bd8b7..ca22dadd8 100644 --- a/lodash-decorators/lodash-decorators.d.ts +++ b/lodash-decorators/lodash-decorators.d.ts @@ -83,31 +83,82 @@ declare module "lodash-decorators" { } 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" { @@ -130,12 +181,26 @@ declare module "lodash-decorators/extensions" { } 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; } From ab13e9f9d6a3b823f7aa4fcbfcbe37a25c969f02 Mon Sep 17 00:00:00 2001 From: tkqubo Date: Sat, 3 Oct 2015 23:55:27 +0900 Subject: [PATCH 3/6] Provide extensible or alternative typings for clarity --- lodash-decorators/lodash-decorators-tests.ts | 50 +++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/lodash-decorators/lodash-decorators-tests.ts b/lodash-decorators/lodash-decorators-tests.ts index 2b1cf8417..7808f802e 100644 --- a/lodash-decorators/lodash-decorators-tests.ts +++ b/lodash-decorators/lodash-decorators-tests.ts @@ -81,17 +81,21 @@ class Person3 { getLastName(): string { return null; } @wrap('getName') - getUpperCaseName(fn: Function) { + getUpperCaseName(fn: () => string): string { return fn().toUpperCase(); } } -const person3 = new Person3('Joe', 'Smith'); +//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' -//FIXME: method signature changed by lodash-decorators -(person3.getUpperCaseName)(); // JOE SMITH +person3.getUpperCaseName(); // JOE SMITH // // Composition @@ -118,10 +122,14 @@ class Person4 { } } -const person4 = new Person4('Joe', 'Smith'); +//by .d.ts author: a workaround to ensure type +interface Person4Ex extends Person4 { + logName(): void; +} -//FIXME: method signature changed by lodash-decorators -(person4.logName)(); // joe-smith +const person4 = new Person4('Joe', 'Smith') as Person4Ex; + +person4.logName(); // joe-smith // // Instance Decorators @@ -152,28 +160,32 @@ class Person6 { constructor() {} private nameList: string[]; - @once.get + //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[] { - //FIXME: Resolve type inconsistency + //MEMO: 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) + @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; //=> [] -//FIXME: method signature changed by lodash-decorators -(person6).names = 'Joe'; //=> ['Joe'] +(person6 as Person6Alt).names = 'Joe'; //=> ['Joe'] person6.names = ['Jim']; //=> ['Jim'] // @@ -202,11 +214,15 @@ class Person7 { } } -const person7 = new Person7('Joe', 'Smith'); +//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 -//FIXME: method signature changed by lodash-decorators -(person7.getUpperCaseName)(); // JOE +person7.getUpperCaseName(); // JOE import { bindAll } from 'lodash-decorators' From 513fd68b2d25d00074843a5a7ff0b8abf6f0bcda Mon Sep 17 00:00:00 2001 From: tkqubo Date: Sat, 3 Oct 2015 23:58:07 +0900 Subject: [PATCH 4/6] Add tscparams --- lodash-decorators/lodash-decorators-tests.ts.tscparams | 1 + 1 file changed, 1 insertion(+) create mode 100644 lodash-decorators/lodash-decorators-tests.ts.tscparams 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 From 92de288aa20c94f2afb1aa76760e18ce6c0bdfc2 Mon Sep 17 00:00:00 2001 From: tkqubo Date: Sun, 4 Oct 2015 01:42:21 +0900 Subject: [PATCH 5/6] Make BindDecorator type-secure --- lodash-decorators/lodash-decorators.d.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lodash-decorators/lodash-decorators.d.ts b/lodash-decorators/lodash-decorators.d.ts index ca22dadd8..daa06627c 100644 --- a/lodash-decorators/lodash-decorators.d.ts +++ b/lodash-decorators/lodash-decorators.d.ts @@ -21,6 +21,10 @@ declare module "lodash-decorators" { (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 { } @@ -73,7 +77,19 @@ declare module "lodash-decorators" { (...args: any[]): MethodDecorator; } export interface BindDecorator { - (...args: any[]): MethodDecorator; + (): 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; From 5ee49c80bc8a6feb730e231ecb8740f36922179b Mon Sep 17 00:00:00 2001 From: tkqubo Date: Sun, 4 Oct 2015 02:23:28 +0900 Subject: [PATCH 6/6] Add lodash-decorators/validate --- lodash-decorators/lodash-decorators-tests.ts | 54 ++++++++++++++++++++ lodash-decorators/lodash-decorators.d.ts | 50 ++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/lodash-decorators/lodash-decorators-tests.ts b/lodash-decorators/lodash-decorators-tests.ts index 7808f802e..60a659c8d 100644 --- a/lodash-decorators/lodash-decorators-tests.ts +++ b/lodash-decorators/lodash-decorators-tests.ts @@ -314,3 +314,57 @@ class Person11 { } } +// +// 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.d.ts b/lodash-decorators/lodash-decorators.d.ts index daa06627c..ac01c5cf6 100644 --- a/lodash-decorators/lodash-decorators.d.ts +++ b/lodash-decorators/lodash-decorators.d.ts @@ -220,3 +220,53 @@ declare module "lodash-decorators/extensions" { 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; +}