Skip to content

Tuple #92

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ It was time to do a full review and refactoring, which results in:

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

Expand Down
3 changes: 0 additions & 3 deletions docs/MIGRATION_TO_V8.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@ this.localStorage.getItem('test', {
})
```

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

### Validation of objects

**`type` option is now required.**
Expand Down
27 changes: 23 additions & 4 deletions docs/VALIDATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,33 +56,52 @@ this.localStorage.getItem('test', { type: 'string' })
```typescript
this.localStorage.getItem('test', {
type: 'array',
items: { type: 'boolean' }
items: { type: 'boolean' },
})
```

```typescript
this.localStorage.getItem('test', {
type: 'array',
items: { type: 'integer' }
items: { type: 'integer' },
})
```

```typescript
this.localStorage.getItem('test', {
type: 'array',
items: { type: 'number' }
items: { type: 'number' },
})
```

```typescript
this.localStorage.getItem('test', {
type: 'array',
items: { type: 'string' }
items: { type: 'string' },
})
```

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

## Tuples

In most cases, an array is for a list with values of the *same type*.
In special cases, it can be useful to use arrays with values of different types.
It's called tuples in TypeScript. For example: `['test', 1]`

```typescript
this.localStorage.getItem('test', {
type: 'array',
items: [
{ type: 'string' },
{ type: 'number' },
],
})
```

Note a tuple has a fixed length: the number of values in the array and the number of schemas provided in `items`
must be exactly the same, otherwise the validation fails.

## How to validate objects

For example:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,9 @@ export interface JSONSchemaArray {
type: 'array';

/**
* Schema for the values of an array.
* Schema for the values of an array, or array of schemas for a tuple.
*/
items: JSONSchema;
items: JSONSchema | JSONSchema[];

/**
* Check if an array length is lower or equal to this value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,42 @@ describe(`JSONValidator`, () => {

});

describe('tuple', () => {

it(`valid`, () => {

const test = jsonValidator.validate(['test1', 1], { type: 'array', items: [{ type: 'string' }, { type: 'number' }] });

expect(test).toBe(true);

});

it(`invalid`, () => {

const test = jsonValidator.validate(['test1', 'test'], { type: 'array', items: [{ type: 'string' }, { type: 'number' }] });

expect(test).toBe(false);

});

it(`special case: greater length`, () => {

const test = jsonValidator.validate(['test1', 1, 2], { type: 'array', items: [{ type: 'string' }, { type: 'number' }] });

expect(test).toBe(false);

});

it(`special case: lower length`, () => {

const test = jsonValidator.validate(['test1'], { type: 'array', items: [{ type: 'string' }, { type: 'number' }] });

expect(test).toBe(false);

});

});

describe('arrays items', () => {

it(`valid`, () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export class JSONValidator {
* Validate a string
* @param data Data to validate
* @param schema Schema describing the string
* @returns If data is valid: `true`, if it is invalid: `false`
*/
private validateString(data: any, schema: JSONSchemaString): boolean {

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

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

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

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

}

/* Specific test for tuples */
if (Array.isArray(schema.items)) {

return this.validateTuple(data, schema.items);

}

/* Validate all the values in array */
for (const value of data) {

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

}

/**
* Validate a tuple (array with fixed length and multiple types)
* @param data Data to validate
* @param schemas Schemas describing the tuple
* @returns If data is valid: `true`, if it is invalid: `false`
*/
private validateTuple(data: any[], schemas: JSONSchema[]): boolean {

/* Tuples have a fixed length */
if (data.length !== schemas.length) {

return false;

}

for (let i = 0; i < schemas.length; i += 1) {

if (!this.validate(data[i], schemas[i])) {
return false;
}

}

return true;

}

/**
* Validate an object
* @param data Data to validate
* @param schema JSON schema describing the object
* @returns If data is valid: `true`, if it is invalid: `false`
*/
private validateObject(data: { [k: string]: any; }, schema: JSONSchemaObject): boolean {

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

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

Expand Down