Skip to content

Commit 5300a2e

Browse files
committed
feat(all): initial work for paramless decorators
1 parent 3b8ac5d commit 5300a2e

11 files changed

Lines changed: 103 additions & 35 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ node_modules
1010
/test
1111
/buildDocs
1212
src/**/*.js
13-
src/**/*.map
13+
src/**/*.map
14+
.history

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"typescript.tsdk": "node_modules/typescript/lib"
3+
}

src/attempt.spec.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,23 @@ describe('attempt', () => {
2121
expect(myClass.fn()).to.be.an.instanceOf(Error);
2222
expect(myClass.fn2()).to.equal(10);
2323
});
24-
});
24+
25+
it('should catch the error and return it (paramless)', () => {
26+
class MyClass {
27+
@Attempt
28+
fn() {
29+
throw new Error();
30+
}
31+
32+
@Attempt
33+
fn2() {
34+
return 10;
35+
}
36+
}
37+
38+
const myClass = new MyClass();
39+
40+
expect(myClass.fn()).to.be.an.instanceOf(Error);
41+
expect(myClass.fn2()).to.equal(10);
42+
});
43+
});

src/attempt.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
import attempt = require('lodash/attempt');
22
import partial = require('lodash/partial');
33

4-
import { DecoratorConfig, DecoratorFactory, LodashMethodDecorator } from './factory';
4+
import { DecoratorConfig, DecoratorFactory, TypedMethodDecorator } from './factory';
55
import { PreValueApplicator } from './applicators';
66

77
const attemptFn = (fn: () => void) => partial(attempt, fn);
8-
const decorator = DecoratorFactory.createDecorator(
9-
new DecoratorConfig(attemptFn, new PreValueApplicator())
10-
);
118

129
/**
1310
* Attempts to invoke func, returning either the result or the caught error object. Any additional arguments are provided to func when it's invoked.
@@ -30,8 +27,10 @@ const decorator = DecoratorFactory.createDecorator(
3027
* myClass.fn(10); // => 10;
3128
* myClass.fn(null); // => Error
3229
*/
33-
export function Attempt(...partials: any[]): LodashMethodDecorator {
34-
return decorator(...partials);
35-
}
30+
export const Attempt = DecoratorFactory.createDecorator(
31+
new DecoratorConfig(attemptFn, new PreValueApplicator(), {
32+
optionalParams: true
33+
})
34+
) as TypedMethodDecorator;
3635
export { Attempt as attempt };
37-
export default decorator;
36+
export default Attempt;

src/bind.spec.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,24 @@ describe('bind', () => {
4040
myClass.fn.call(null);
4141
expect(context).to.equal(myClass);
4242
});
43-
});
43+
44+
it('should bind without params', () => {
45+
let context;
46+
47+
class MyClass {
48+
@Bind
49+
fn() {
50+
context = this;
51+
}
52+
}
53+
54+
const myClass = new MyClass();
55+
const myClass2 = new MyClass();
56+
57+
myClass.fn.call(null);
58+
expect(context).to.equal(myClass);
59+
60+
myClass2.fn.call(null);
61+
expect(context).to.equal(myClass2);
62+
});
63+
});

src/bind.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import bind = require('lodash/bind');
22

3-
import { DecoratorConfig, DecoratorFactory, LodashMethodDecorator } from './factory';
3+
import { DecoratorConfig, DecoratorFactory, TypedMethodDecorator1 } from './factory';
44
import { BindApplicator } from './applicators';
55

6-
const decorator = DecoratorFactory.createInstanceDecorator(
7-
new DecoratorConfig(bind, new BindApplicator())
8-
);
9-
106
/**
117
* Creates a function that invokes func with the this binding of thisArg and partials prepended to the arguments it receives.
128
*
@@ -32,8 +28,10 @@ const decorator = DecoratorFactory.createInstanceDecorator(
3228
* myClass.bound.call(null); // => myClass {}
3329
* myClass.unbound.call(null); // => null
3430
*/
35-
export function Bind(...partials: any[]): LodashMethodDecorator {
36-
return decorator(...partials);
37-
}
38-
export { Bind as bind };
39-
export default decorator;
31+
export const Bind = DecoratorFactory.createInstanceDecorator(
32+
new DecoratorConfig(bind, new BindApplicator(), {
33+
optionalParams: true
34+
})
35+
) as TypedMethodDecorator1<any>;
36+
export { Bind as bind, };
37+
export default Bind;

src/curry.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,21 @@ describe('curry', () => {
1818
expect(add5(10)).to.equal(15);
1919
});
2020

21+
it('should curry the method with default arity (paramless)', () => {
22+
class MyClass {
23+
@Curry
24+
add(a: any, b?: any) {
25+
return a + b;
26+
}
27+
}
28+
29+
const myClass = new MyClass();
30+
const add5 = myClass.add(5);
31+
32+
expect(add5).to.be.an.instanceOf(Function);
33+
expect(add5(10)).to.equal(15);
34+
});
35+
2136
it('should curry the method with fixed arity', () => {
2237
class MyClass {
2338
@Curry(2)

src/curry.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import curry = require('lodash/curry');
22

3-
import { DecoratorConfig, DecoratorFactory, LodashMethodDecorator } from './factory';
3+
import { DecoratorConfig, DecoratorFactory, TypedMethodDecorator1 } from './factory';
44
import { PreValueApplicator } from './applicators';
55

6-
const decorator = DecoratorFactory.createInstanceDecorator(
7-
new DecoratorConfig(curry, new PreValueApplicator(), { bound: true })
8-
);
9-
106
/**
117
* Creates a function that accepts arguments of func and either invokes func returning its result, if at least arity number of arguments have been provided, or returns a function that accepts the remaining func arguments, and so on.
128
* The arity of func may be specified if func.length is not sufficient.
@@ -33,8 +29,8 @@ const decorator = DecoratorFactory.createInstanceDecorator(
3329
*
3430
* add5AndMultiply(10); // => 30
3531
*/
36-
export function Curry(arity?: number): LodashMethodDecorator {
37-
return decorator(arity);
38-
}
32+
export const Curry = DecoratorFactory.createInstanceDecorator(
33+
new DecoratorConfig(curry, new PreValueApplicator(), { bound: true, optionalParams: true })
34+
) as TypedMethodDecorator1<number>;
3935
export { Curry as curry };
40-
export default decorator;
36+
export default Curry;

src/factory/DecoratorConfig.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface DecoratorConfigOptions {
66
getter?: boolean;
77
property?: boolean;
88
method?: boolean;
9+
optionalParams?: boolean;
910
}
1011

1112
export class DecoratorConfig {
@@ -34,4 +35,8 @@ export class DecoratorConfig {
3435
get method(): boolean {
3536
return this.options.method != null ? this.options.method : true;
3637
}
37-
}
38+
39+
get optionalParams(): boolean {
40+
return this.options.optionalParams != null ? this.options.optionalParams : false;
41+
}
42+
}

src/factory/DecoratorFactory.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ export type GenericDecorator = (...args: any[]) => LodashDecorator;
1212

1313
export class InternalDecoratorFactory {
1414
createDecorator(config: DecoratorConfig): GenericDecorator {
15-
const { applicator } = config;
15+
const { applicator, optionalParams } = config;
1616

1717
return (...args: any[]): LodashDecorator => {
18-
return (target: Object, name: string, _descriptor?: PropertyDescriptor): PropertyDescriptor => {
18+
const decorator = (target: Object, name: string, _descriptor?: PropertyDescriptor): PropertyDescriptor => {
1919
const descriptor = this._resolveDescriptor(target, name, _descriptor);
2020
const { value, get, set } = descriptor;
2121

@@ -33,14 +33,16 @@ export class InternalDecoratorFactory {
3333

3434
return descriptor;
3535
};
36+
37+
return optionalParams && args.length >= 2 ? decorator(args[0], args[1], args[2]) as any : decorator;
3638
};
3739
}
3840

3941
createInstanceDecorator(config: DecoratorConfig): GenericDecorator {
40-
const { applicator, bound } = config;
42+
const { applicator, bound, optionalParams } = config;
4143

4244
return (...args: any[]): LodashDecorator => {
43-
return (target: Object, name: string, _descriptor?: PropertyDescriptor): PropertyDescriptor => {
45+
const decorator = (target: Object, name: string, _descriptor?: PropertyDescriptor): PropertyDescriptor => {
4446
const descriptor = this._resolveDescriptor(target, name, _descriptor);
4547
const { value, writable, enumerable, configurable, get, set } = descriptor;
4648
const isFirstInstance = !InstanceChainMap.has([ target, name ]);
@@ -146,6 +148,8 @@ export class InternalDecoratorFactory {
146148

147149
return descriptor;
148150
};
151+
152+
return optionalParams && args.length >= 2 ? decorator(args[0], args[1], args[2]) as any: decorator;
149153
};
150154
}
151155

0 commit comments

Comments
 (0)