Skip to content

Commit 0330e84

Browse files
committed
feat: add intersection contract
1 parent e870ce8 commit 0330e84

File tree

4 files changed

+208
-0
lines changed

4 files changed

+208
-0
lines changed

__tests__/contracts/intersection.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* @flow */
2+
3+
import { intersection } from '../../src/contracts/intersection';
4+
import { ValidationError } from '../../src/ValidationError';
5+
6+
const createError = (...types: $ReadOnlyArray<string>) => (
7+
name: string,
8+
value: mixed,
9+
) => new ValidationError(name, value, types);
10+
11+
describe('intersection', () => {
12+
describe('Creates new Contract for one or more other contracts or validation functions', () => {
13+
it('Return ValidationError if at least one Contract returns ValidationError', () => {
14+
const validate1 = jest.fn().mockReturnValue({ a: 1, b: 2 });
15+
const validate2 = jest.fn().mockReturnValue({ a: 1, b: 2 });
16+
const validate3 = jest.fn(createError('type3'));
17+
18+
const result = intersection(
19+
validate1,
20+
validate2,
21+
validate3,
22+
)('valueName', { a: 1, b: 2 });
23+
24+
expect(result).toBeInstanceOf(ValidationError);
25+
expect(result.expectedTypes).toEqual(['Intersection']);
26+
expect(result.nested).toEqual([
27+
new ValidationError('valueName', { a: 1, b: 2 }, 'type3'),
28+
]);
29+
30+
expect(validate1).lastCalledWith('valueName', { a: 1, b: 2 });
31+
expect(validate2).lastCalledWith('valueName', { a: 1, b: 2 });
32+
expect(validate3).lastCalledWith('valueName', { a: 1, b: 2 });
33+
});
34+
35+
it('Return value in other cases', () => {
36+
const validate1 = jest.fn().mockReturnValue({ a: 1, b: 2 });
37+
const validate2 = jest.fn().mockReturnValue({ a: 1, b: 2 });
38+
const validate3 = jest.fn().mockReturnValue({ a: 1, b: 2 });
39+
40+
expect(
41+
intersection(
42+
validate1,
43+
validate2,
44+
validate3,
45+
)('valueName', { a: 1, b: 2 }),
46+
).toEqual({ a: 1, b: 2 });
47+
48+
expect(validate1).lastCalledWith('valueName', { a: 1, b: 2 });
49+
expect(validate2).lastCalledWith('valueName', { a: 1, b: 2 });
50+
expect(validate3).lastCalledWith('valueName', { a: 1, b: 2 });
51+
});
52+
});
53+
});

src/contracts/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
export * from './array';
44
export * from './boolean';
5+
export * from './intersection';
56
export * from './literal';
67
export * from './null';
78
export * from './number';

src/contracts/intersection.js

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/* @flow */
2+
3+
import { ValidationError } from '../ValidationError';
4+
import * as contract from '../Contract';
5+
6+
export class IntersectionError extends ValidationError {
7+
constructor(
8+
valueName: string,
9+
value: mixed,
10+
errors: $ReadOnlyArray<ValidationError>,
11+
) {
12+
super(valueName, value, 'Intersection', errors);
13+
this.name = 'IntersectionError';
14+
}
15+
}
16+
17+
declare export function intersection<A>(
18+
a: (name: string, value: mixed) => ValidationError | A,
19+
): contract.Contract<A>;
20+
21+
declare export function intersection<A, B>(
22+
a: (name: string, value: mixed) => ValidationError | A,
23+
b: (name: string, value: mixed) => ValidationError | B,
24+
): contract.Contract<A & B>;
25+
26+
declare export function intersection<A, B, C>(
27+
a: (name: string, value: mixed) => ValidationError | A,
28+
b: (name: string, value: mixed) => ValidationError | B,
29+
c: (name: string, value: mixed) => ValidationError | C,
30+
): contract.Contract<A & B & C>;
31+
32+
declare export function intersection<A, B, C, D>(
33+
a: (name: string, value: mixed) => ValidationError | A,
34+
b: (name: string, value: mixed) => ValidationError | B,
35+
c: (name: string, value: mixed) => ValidationError | C,
36+
d: (name: string, value: mixed) => ValidationError | D,
37+
): contract.Contract<A & B & C & D>;
38+
39+
declare export function intersection<A, B, C, D, E>(
40+
a: (name: string, value: mixed) => ValidationError | A,
41+
b: (name: string, value: mixed) => ValidationError | B,
42+
c: (name: string, value: mixed) => ValidationError | C,
43+
d: (name: string, value: mixed) => ValidationError | D,
44+
e: (name: string, value: mixed) => ValidationError | E,
45+
): contract.Contract<A & B & C & D & E>;
46+
47+
declare export function intersection<A, B, C, D, E, F>(
48+
a: (name: string, value: mixed) => ValidationError | A,
49+
b: (name: string, value: mixed) => ValidationError | B,
50+
c: (name: string, value: mixed) => ValidationError | C,
51+
d: (name: string, value: mixed) => ValidationError | D,
52+
e: (name: string, value: mixed) => ValidationError | E,
53+
f: (name: string, value: mixed) => ValidationError | F,
54+
): contract.Contract<A & B & C & D & E & F>;
55+
56+
declare export function intersection<A, B, C, D, E, F, G>(
57+
a: (name: string, value: mixed) => ValidationError | A,
58+
b: (name: string, value: mixed) => ValidationError | B,
59+
c: (name: string, value: mixed) => ValidationError | C,
60+
d: (name: string, value: mixed) => ValidationError | D,
61+
e: (name: string, value: mixed) => ValidationError | E,
62+
f: (name: string, value: mixed) => ValidationError | F,
63+
g: (name: string, value: mixed) => ValidationError | G,
64+
): contract.Contract<A & B & C & D & E & F & G>;
65+
66+
declare export function intersection<A, B, C, D, E, F, G, H>(
67+
a: (name: string, value: mixed) => ValidationError | A,
68+
b: (name: string, value: mixed) => ValidationError | B,
69+
c: (name: string, value: mixed) => ValidationError | C,
70+
d: (name: string, value: mixed) => ValidationError | D,
71+
e: (name: string, value: mixed) => ValidationError | E,
72+
f: (name: string, value: mixed) => ValidationError | F,
73+
g: (name: string, value: mixed) => ValidationError | G,
74+
h: (name: string, value: mixed) => ValidationError | H,
75+
): contract.Contract<A & B & C & D & E & F & G & H>;
76+
77+
export function intersection<T>(
78+
...rules: $ReadOnlyArray<(name: string, value: mixed) => ValidationError | T>
79+
): contract.Contract<T> {
80+
const validators = rules;
81+
82+
const intersectionContract = (name, value: any): ValidationError | T => {
83+
const errors = validators
84+
.map(validate => validate(name, value))
85+
.reduce((scope, error) => {
86+
if (error instanceof ValidationError) scope.push(error);
87+
return scope;
88+
}, []);
89+
90+
return errors.length ? new IntersectionError(name, value, errors) : value;
91+
};
92+
93+
return contract.of(intersectionContract);
94+
}

src/index.d.ts

+60
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,66 @@ export declare var str: typeof string;
112112
export declare var isStr: typeof string;
113113
export declare var passStr: typeof string;
114114

115+
export declare function intersection<A>(
116+
a: Validator<A>,
117+
): Contract<A>;
118+
119+
export declare function intersection<A, B>(
120+
a: Validator<A>,
121+
b: Validator<B>,
122+
): Contract<A & B>;
123+
124+
export declare function intersection<A, B, C>(
125+
a: Validator<A>,
126+
b: Validator<B>,
127+
c: Validator<C>,
128+
): Contract<A & B & C>;
129+
130+
export declare function intersection<A, B, C, D>(
131+
a: Validator<A>,
132+
b: Validator<B>,
133+
c: Validator<C>,
134+
d: Validator<D>,
135+
): Contract<A & B & C & D>;
136+
137+
export declare function intersection<A, B, C, D, E>(
138+
a: Validator<A>,
139+
b: Validator<B>,
140+
c: Validator<C>,
141+
d: Validator<D>,
142+
e: Validator<E>,
143+
): Contract<A & B & C & D & E>;
144+
145+
export declare function intersection<A, B, C, D, E, F>(
146+
a: Validator<A>,
147+
b: Validator<B>,
148+
c: Validator<C>,
149+
d: Validator<D>,
150+
e: Validator<E>,
151+
f: Validator<F>,
152+
): Contract<A & B & C & D & E & F>;
153+
154+
export declare function intersection<A, B, C, D, E, F, G>(
155+
a: Validator<A>,
156+
b: Validator<B>,
157+
c: Validator<C>,
158+
d: Validator<D>,
159+
e: Validator<E>,
160+
f: Validator<F>,
161+
g: Validator<G>,
162+
): Contract<A & B & C & D & E & F & G>;
163+
164+
export declare function intersection<A, B, C, D, E, F, G, H>(
165+
a: Validator<A>,
166+
b: Validator<B>,
167+
c: Validator<C>,
168+
d: Validator<D>,
169+
e: Validator<E>,
170+
f: Validator<F>,
171+
g: Validator<G>,
172+
h: Validator<H>,
173+
): Contract<A & B & C & D & E & F & G & H>;
174+
115175
export declare function union<T extends Array<Validator<any> | string | number | boolean>>(
116176
...rules: T
117177
): Contract<

0 commit comments

Comments
 (0)