Skip to content

Commit 842bda8

Browse files
committed
Merge branch 'release/0.5.0'
2 parents 4931327 + cdf64a0 commit 842bda8

File tree

4 files changed

+147
-125
lines changed

4 files changed

+147
-125
lines changed

README.md

Lines changed: 73 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,7 @@
44

55
# Validate Objects Against TypeScript Interfaces #
66

7-
- [Installation](#installation)
8-
- [Basic Usage](#basic-usage)
9-
- [Assertions](#assertions)
10-
- [Handling Validation Errors](#handling-validation-errors)
11-
- [Documentation](#documentation)
12-
- [Validator](#validator)
13-
- [extendValidator](#extendvalidator)
14-
- [validate](#validate)
15-
- [assertThat](#assertthat)
16-
- [optional](#optional)
17-
- [nullable](#nullable)
18-
- [defaultsTo](#defaultsto)
19-
- [onErrorDefaultsTo](#onerrordefaultsto)
20-
- [isBoolean](#isboolean)
21-
- [isNumber](#isnumber)
22-
- [min](#min)
23-
- [max](#max)
24-
- [isString](#isstring)
25-
- [matches](#matches)
26-
- [minLength](#minLength)
27-
- [maxLength](#maxLength)
28-
- [lengthIs](#lengthis)
29-
- [isArray](#isarray)
30-
- [eachItem](#eachitem)
31-
- [isObject](#isobject)
32-
- [conformsTo](#conformsto)
33-
- [equals](#equals)
7+
Builds strongly-typed validators that can prove to the TypeScript compiler that a given object conforms to a TypeScript interface.
348

359
## Installation ##
3610

@@ -49,10 +23,10 @@ interface Employee {
4923

5024
// 2) Define a schema
5125
const employeeValidator: Validator<Employee> = {
52-
name: assertThat('name', isString(minLength(1))),
53-
roleCode: assertThat('roleCode', isNumber(min(1, max(10)))),
54-
completedTraining: assertThat('completedTraining', optional(isBoolean())),
55-
addressPostcode: assertThat('addressPostcode', isString(matches(/^[a-z]{2}\d{1,2}\s+\d{1,2}[a-z]{2}$/i)))
26+
name: isString(minLength(1)),
27+
roleCode: isNumber(min(1, max(10))),
28+
completedTraining: optional(isBoolean()),
29+
addressPostcode: isString(matches(/^[a-z]{2}\d{1,2}\s+\d{1,2}[a-z]{2}$/i))
5630
};
5731

5832
// 3) Validate
@@ -68,36 +42,50 @@ try {
6842
let wrong: Employee = validate({
6943
name: 'Name',
7044
roleCode: 4,
71-
completedTraining: false,
45+
completedTraining: 'false',
7246
addressPostcode: 'WRONG'
7347
}, employeeValidator);
7448
} catch (err) {
7549
console.log(err.toString());
7650
}
7751

7852
// Outputs:
79-
// Validation failed for $root.addressPostcode: Failed regular expression /^[a-z]{2}\d{1,2}\s+\d{1,2}[a-z]{2}$/i
53+
// 2 validation errors:
54+
// $root.completedTraining: Expected boolean, got string
55+
// $root.addressPostcode: Failed regular expression /^[a-z]{2}\d{1,2}\s+\d{1,2}[a-z]{2}$/i
8056

8157
```
8258

83-
## Assertions ##
59+
## Documentation ##
8460
This library provides a number of strongly-typed assertions which can be combined to validate the type of each property.
8561

86-
An assertion may take another assertion as its last argument; if assertion check passes, it calls the next assertion. For example, `isString(minLength(1, maxLength(10)))` first checks if the value is a string, then checks if its length is at least 1, and then checks that its length is no more than 10. If `isString` fails, `minLength` isn't run. Chaining assertions in this way allows for complex values to be validated.
87-
88-
Some assertions require other assertions to come before it. For example, `minLength` can't be used by itself because it needs another assertion to check that the value has the `length` property - so something like `isString(minLength(1))` or `isArray(minLength(0))`.
89-
90-
## Handling Validation Errors ##
91-
92-
Errors will always be of the type `ValidationError`, which has a number of useful properties:
93-
94-
- `errorCode`: A string which is one of a set of error codes, e.g. `NOT_STRING`. Useful for producing custom error messages or triggering certain error logic.
95-
- `message`: A human-readable error message, with more information as to why the validation failed
96-
- `path`: An array of objects that describe the path to the value that caused the validation to fail. Each object is either an `ArrayIndexPathNode` (which has an `index` property) or `KeyPathNode` (which has a `key` property).
97-
98-
There's a `toString` method which prints this information in a human-readable format.
99-
100-
## Documentation ##
62+
An assertion may take another assertion as its last argument; if assertion check passes, it calls the next assertion. For example, `isString(minLength(1, maxLength(10)))` first checks if the value is a string, then checks if its length is at least 1, and then checks that its length is no more than 10. If `isString` fails, `minLength` isn't run. Chaining assertions in this way allows for complex validation.
63+
64+
Some assertions require other assertions to come before it. For example, `minLength` can't be used by itself because it needs another assertion to check that the value has the `length` property - so something like `isString(minLength(1))` or `isArray(minLength(1))`.
65+
66+
Jump to section:
67+
- [Validator](#validator)
68+
- [extendValidator](#extendvalidator)
69+
- [validate](#validate)
70+
- [optional](#optional)
71+
- [nullable](#nullable)
72+
- [defaultsTo](#defaultsto)
73+
- [onErrorDefaultsTo](#onerrordefaultsto)
74+
- [isBoolean](#isboolean)
75+
- [isNumber](#isnumber)
76+
- [min](#min)
77+
- [max](#max)
78+
- [isString](#isstring)
79+
- [matches](#matches)
80+
- [minLength](#minLength)
81+
- [maxLength](#maxLength)
82+
- [lengthIs](#lengthis)
83+
- [isArray](#isarray)
84+
- [eachItem](#eachitem)
85+
- [isObject](#isobject)
86+
- [conformsTo](#conformsto)
87+
- [equals](#equals)
88+
- [Handling Validation Errors](#handling-validation-errors)
10189

10290
### Validator ###
10391

@@ -111,25 +99,25 @@ interface IFoo {
11199

112100
// A valid validator
113101
const fooValidator: Validator<IFoo> = {
114-
bar: assertThat('bar', isString()),
115-
baz: assertThat('baz', isNumber())
102+
bar: isString(),
103+
baz: isNumber()
116104
};
117105

118106
// All of these are invalid, and will result in an error from the TypeScript compiler
119107

120108
const fooValidator: Validator<IFoo> = {
121-
bar: assertThat('bar', isString())
109+
bar: isString()
122110
}; // Missing 'baz'
123111

124112
const fooValidator: Validator<IFoo> = {
125-
bar: assertThat('bar', isNumber()), // Wrong type
126-
baz: assertThat('baz', isNumber())
113+
bar: isNumber(), // Wrong type
114+
baz: isNumber()
127115
};
128116

129117
const fooValidator: Validator<IFoo> = {
130-
bar: assertThat('bar', isString()),
131-
baz: assertThat('baz', isNumber()),
132-
blah: assertThat('blah', isBoolean()) // Unexpected property
118+
bar: isString(),
119+
baz: isNumber(),
120+
blah: isBoolean() // Unexpected property
133121
};
134122

135123
```
@@ -145,15 +133,15 @@ interface IFoo {
145133
}
146134

147135
const fooValidator: Validator<IFoo> = {
148-
abc: assertThat('abc', isNumber())
136+
abc: isNumber()
149137
};
150138

151139
interface IBar extends IFoo {
152140
xyz: string;
153141
}
154142

155143
const barValidator: Validator<IBar> = extendValidator(fooValidator, {
156-
xyz: assertThat('xyz', isString())
144+
xyz: isString()
157145
});
158146
```
159147

@@ -162,9 +150,6 @@ const barValidator: Validator<IBar> = extendValidator(fooValidator, {
162150

163151
Checks that `arg` conforms to the type `T` using the given `validator`. Returns an object that conforms to `T` or throws an error.
164152

165-
### assertThat ###
166-
Used to start an assertion chain.
167-
168153
### optional ###
169154
Used when the property may not present on the object, or its value is undefined. Example:
170155

@@ -174,7 +159,7 @@ interface IFoo {
174159
}
175160

176161
const fooValidator: Validator<IFoo> = {
177-
bar: assertThat('bar', optional(isString())),
162+
bar: optional(isString()),
178163
};
179164

180165
// Both of these are acceptable
@@ -202,7 +187,7 @@ interface IFoo {
202187
}
203188

204189
const fooValidator: Validator<IFoo> = {
205-
bar: assertThat('bar', nullable(isString())),
190+
bar: nullable(isString()),
206191
};
207192
```
208193

@@ -216,7 +201,7 @@ interface IFoo {
216201
}
217202

218203
const fooValidator: Validator<IFoo> = {
219-
bar: assertThat('bar', defaultsTo('baz', isString())),
204+
bar: defaultsTo('baz', isString()),
220205
};
221206

222207
const foo = validate({}, fooValidator);
@@ -236,14 +221,15 @@ interface IFoo {
236221
}
237222

238223
const fooValidator: Validator<IFoo> = {
239-
bar: assertThat('bar', onErrorDefaultsTo('baz', isString())),
224+
bar: onErrorDefaultsTo('baz', isString()),
240225
};
241226

242227
const foo = validate({bar: 123}, fooValidator);
243228

244229
console.log(foo);
245230
// {bar: 'baz'}
246231
```
232+
247233
### isBoolean ###
248234
Throws an error if the value is not a boolean.
249235

@@ -259,7 +245,7 @@ interface IFoo {
259245
}
260246

261247
const fooValidator: Validator<IFoo> = {
262-
bar: assertThat('bar', isNuber(min(0)))
248+
bar: isNuber(min(0))
263249
};
264250

265251
// This will throw an error
@@ -281,7 +267,7 @@ interface IFoo {
281267
}
282268

283269
const fooValidator: Validator<IFoo> = {
284-
bar: assertThat('bar', isStirng(matches(/^[a-z]+$/)))
270+
bar: isStirng(matches(/^[a-z]+$/))
285271
};
286272

287273
// This will throw an error
@@ -297,7 +283,7 @@ interface IFoo {
297283
}
298284

299285
const fooValidator: Validator<IFoo> = {
300-
bar: assertThat('bar', isStirng(minLength(1)))
286+
bar: isStirng(minLength(1))
301287
};
302288

303289
// This will throw an error
@@ -319,7 +305,7 @@ interface IFoo {
319305
}
320306

321307
const fooValidator: Validator<IFoo> = {
322-
bar: assertThat('bar', isArray())
308+
bar: isArray()
323309
};
324310

325311
// This is valid
@@ -342,7 +328,7 @@ interface IFoo {
342328
}
343329

344330
const fooValidator: Validator<IFoo> = {
345-
bar: assertThat('bar', isArray(eachItem(isNumber())))
331+
bar: isArray(eachItem(isNumber()))
346332
};
347333

348334
// This is valid
@@ -365,7 +351,7 @@ interface IFoo {
365351
}
366352

367353
const fooValidator: Validator<IFoo> = {
368-
bar: assertThat('bar', isObject())
354+
bar: isObject()
369355
};
370356

371357
// This is valid, but it wouldn't be safe to access properties on foo.bar
@@ -392,11 +378,11 @@ interface IFoo {
392378
}
393379

394380
const barValidator: Validator<IBar> = {
395-
baz: assertThat('baz', isNumber())
381+
baz: isNumber()
396382
};
397383

398384
const fooValidator: Validator<IFoo> = {
399-
bar: assertThat('bar', conformsTo(barValidator))
385+
bar: conformsTo(barValidator)
400386
};
401387

402388
// This is valid
@@ -425,11 +411,23 @@ interface IFoo {
425411
}
426412

427413
const fooValidator: Validator<IFoo> = {
428-
bar: assertThat('bar', equals<Bar>('A', 'B', 'C'))
414+
bar: equals<Bar>('A', 'B', 'C')
429415
};
430416

431417
// Throws an error
432418
validate({
433419
bar: 'D'
434420
}, fooValidator);
435421
```
422+
423+
### Handling Validation Errors ###
424+
425+
Errors will always be of the type `ValidationErrorCollection`, which has a property `error: ValidationError[]`.
426+
427+
The `ValidationError` type has a number of useful properties:
428+
429+
- `errorCode`: A string which is one of a set of error codes, e.g. `NOT_STRING`. Useful for producing custom error messages or triggering certain error logic.
430+
- `message`: A human-readable error message, with more information as to why the validation failed
431+
- `path`: An array of objects that describe the path to the value that caused the validation to fail. Each object is either an `ArrayIndexPathNode` (which has an `index` property) or `KeyPathNode` (which has a `key` property).
432+
433+
The `ValidationErrorCollection.toString()` method prints this information in a human-readable format. The name of the root object defaults to `$root`, but this can be changed by passing a string, e.g. `err.toString('this')`.

0 commit comments

Comments
 (0)