Skip to content

Commit e568328

Browse files
authored
Tuple (#92)
* feat: reintroduce tuples
1 parent f0dcb18 commit e568328

File tree

6 files changed

+102
-10
lines changed

6 files changed

+102
-10
lines changed

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ It was time to do a full review and refactoring, which results in:
3333

3434
- **`type` now required for array, object, const and enum validation schemas**
3535
- `JSONSchemaNull` removed (useless, `null` doesn't require any validation)
36-
- `items` in arrays schemas no longer accepts an array of JSON schemas
3736
- `JSONSchema` no longer accepts extra properties
3837
- `getUnsafeItem()` is removed (was already deprecated in v7)
3938

docs/MIGRATION_TO_V8.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,6 @@ this.localStorage.getItem('test', {
6767
})
6868
```
6969

70-
Also, `items` no longer accepts an array of JSON schemas, meaning arrays with multiple types
71-
are no longer possible (and it's better for consistency, use an object if you mix types in a list).
72-
7370
### Validation of objects
7471

7572
**`type` option is now required.**

docs/VALIDATION.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,33 +56,52 @@ this.localStorage.getItem('test', { type: 'string' })
5656
```typescript
5757
this.localStorage.getItem('test', {
5858
type: 'array',
59-
items: { type: 'boolean' }
59+
items: { type: 'boolean' },
6060
})
6161
```
6262

6363
```typescript
6464
this.localStorage.getItem('test', {
6565
type: 'array',
66-
items: { type: 'integer' }
66+
items: { type: 'integer' },
6767
})
6868
```
6969

7070
```typescript
7171
this.localStorage.getItem('test', {
7272
type: 'array',
73-
items: { type: 'number' }
73+
items: { type: 'number' },
7474
})
7575
```
7676

7777
```typescript
7878
this.localStorage.getItem('test', {
7979
type: 'array',
80-
items: { type: 'string' }
80+
items: { type: 'string' },
8181
})
8282
```
8383

8484
What's expected in `items` is another JSON schema.
8585

86+
## Tuples
87+
88+
In most cases, an array is for a list with values of the *same type*.
89+
In special cases, it can be useful to use arrays with values of different types.
90+
It's called tuples in TypeScript. For example: `['test', 1]`
91+
92+
```typescript
93+
this.localStorage.getItem('test', {
94+
type: 'array',
95+
items: [
96+
{ type: 'string' },
97+
{ type: 'number' },
98+
],
99+
})
100+
```
101+
102+
Note a tuple has a fixed length: the number of values in the array and the number of schemas provided in `items`
103+
must be exactly the same, otherwise the validation fails.
104+
86105
## How to validate objects
87106

88107
For example:

projects/ngx-pwa/local-storage/src/lib/validation/json-schema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,9 @@ export interface JSONSchemaArray {
166166
type: 'array';
167167

168168
/**
169-
* Schema for the values of an array.
169+
* Schema for the values of an array, or array of schemas for a tuple.
170170
*/
171-
items: JSONSchema;
171+
items: JSONSchema | JSONSchema[];
172172

173173
/**
174174
* Check if an array length is lower or equal to this value.

projects/ngx-pwa/local-storage/src/lib/validation/json-validation.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,42 @@ describe(`JSONValidator`, () => {
534534

535535
});
536536

537+
describe('tuple', () => {
538+
539+
it(`valid`, () => {
540+
541+
const test = jsonValidator.validate(['test1', 1], { type: 'array', items: [{ type: 'string' }, { type: 'number' }] });
542+
543+
expect(test).toBe(true);
544+
545+
});
546+
547+
it(`invalid`, () => {
548+
549+
const test = jsonValidator.validate(['test1', 'test'], { type: 'array', items: [{ type: 'string' }, { type: 'number' }] });
550+
551+
expect(test).toBe(false);
552+
553+
});
554+
555+
it(`special case: greater length`, () => {
556+
557+
const test = jsonValidator.validate(['test1', 1, 2], { type: 'array', items: [{ type: 'string' }, { type: 'number' }] });
558+
559+
expect(test).toBe(false);
560+
561+
});
562+
563+
it(`special case: lower length`, () => {
564+
565+
const test = jsonValidator.validate(['test1'], { type: 'array', items: [{ type: 'string' }, { type: 'number' }] });
566+
567+
expect(test).toBe(false);
568+
569+
});
570+
571+
});
572+
537573
describe('arrays items', () => {
538574

539575
it(`valid`, () => {

projects/ngx-pwa/local-storage/src/lib/validation/json-validator.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export class JSONValidator {
4141
* Validate a string
4242
* @param data Data to validate
4343
* @param schema Schema describing the string
44+
* @returns If data is valid: `true`, if it is invalid: `false`
4445
*/
4546
private validateString(data: any, schema: JSONSchemaString): boolean {
4647

@@ -86,6 +87,7 @@ export class JSONValidator {
8687
* Validate a number or an integer
8788
* @param data Data to validate
8889
* @param schema Schema describing the number or integer
90+
* @returns If data is valid: `true`, if it is invalid: `false`
8991
*/
9092
private validateNumber(data: any, schema: JSONSchemaNumber | JSONSchemaInteger): boolean {
9193

@@ -136,6 +138,7 @@ export class JSONValidator {
136138
* Validate a boolean
137139
* @param data Data to validate
138140
* @param schema Schema describing the boolean
141+
* @returns If data is valid: `true`, if it is invalid: `false`
139142
*/
140143
private validateBoolean(data: any, schema: JSONSchemaBoolean): boolean {
141144

@@ -155,6 +158,7 @@ export class JSONValidator {
155158
* Validate an array
156159
* @param data Data to validate
157160
* @param schema Schema describing the array
161+
* @returns If data is valid: `true`, if it is invalid: `false`
158162
*/
159163
private validateArray(data: any[], schema: JSONSchemaArray): boolean {
160164

@@ -181,6 +185,13 @@ export class JSONValidator {
181185

182186
}
183187

188+
/* Specific test for tuples */
189+
if (Array.isArray(schema.items)) {
190+
191+
return this.validateTuple(data, schema.items);
192+
193+
}
194+
184195
/* Validate all the values in array */
185196
for (const value of data) {
186197

@@ -194,10 +205,38 @@ export class JSONValidator {
194205

195206
}
196207

208+
/**
209+
* Validate a tuple (array with fixed length and multiple types)
210+
* @param data Data to validate
211+
* @param schemas Schemas describing the tuple
212+
* @returns If data is valid: `true`, if it is invalid: `false`
213+
*/
214+
private validateTuple(data: any[], schemas: JSONSchema[]): boolean {
215+
216+
/* Tuples have a fixed length */
217+
if (data.length !== schemas.length) {
218+
219+
return false;
220+
221+
}
222+
223+
for (let i = 0; i < schemas.length; i += 1) {
224+
225+
if (!this.validate(data[i], schemas[i])) {
226+
return false;
227+
}
228+
229+
}
230+
231+
return true;
232+
233+
}
234+
197235
/**
198236
* Validate an object
199237
* @param data Data to validate
200238
* @param schema JSON schema describing the object
239+
* @returns If data is valid: `true`, if it is invalid: `false`
201240
*/
202241
private validateObject(data: { [k: string]: any; }, schema: JSONSchemaObject): boolean {
203242

@@ -248,6 +287,7 @@ export class JSONValidator {
248287
* Validate a constant
249288
* @param data Data ta validate
250289
* @param schema JSON schema describing the constant
290+
* @returns If data is valid: `true`, if it is invalid: `false`
251291
*/
252292
private validateConst(data: any, schema: JSONSchemaBoolean | JSONSchemaInteger | JSONSchemaNumber | JSONSchemaString): boolean {
253293

@@ -263,6 +303,7 @@ export class JSONValidator {
263303
* Validate an enum
264304
* @param data Data ta validate
265305
* @param schema JSON schema describing the enum
306+
* @returns If data is valid: `true`, if it is invalid: `false`
266307
*/
267308
private validateEnum(data: any, schema: JSONSchemaInteger | JSONSchemaNumber | JSONSchemaString): boolean {
268309

0 commit comments

Comments
 (0)