Skip to content

feat: new JSONSchema and auto-inference for basic types #74

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

Closed
wants to merge 11 commits into from
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,6 @@ this.localStorage.getItem<User>('user').subscribe((user) => {
});
```

As any data can be stored, you can type your data.

Not finding an item is not an error, it succeeds but returns `null`.

```typescript
Expand All @@ -136,7 +134,7 @@ Starting with *version 5*, you can use a [JSON Schema](http://json-schema.org/)
A [migration guide](./docs/MIGRATION_TO_V7.md) is available.

```typescript
this.localStorage.getItem<string>('test', { schema: { type: 'string' } })
this.localStorage.getItem('test', { schema: { type: 'string' } })
.subscribe((user) => {
// Called if data is valid or null
}, (error) => {
Expand Down
46 changes: 27 additions & 19 deletions docs/VALIDATION.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Validation guide

## Version

This is the up to date guide about validation for version >= 8.
The old guide for validation in versions < 8 is available [here](./VALIDATION_BEFORE_V8.md).

## Why validation?

Any client-side storage (cookies, `localStorage`, `indexedDb`...) is not secure by nature,
Expand Down Expand Up @@ -27,52 +32,52 @@ as you'll see the more complex it is, the more complex is validation too.
### Boolean

```typescript
this.localStorage.getItem<boolean>('test', { schema: { type: 'boolean' } })
this.localStorage.getItem('test', { schema: { type: 'boolean' } })
```

### Integer

```typescript
this.localStorage.getItem<number>('test', { schema: { type: 'integer' } })
this.localStorage.getItem('test', { schema: { type: 'integer' } })
```

### Number

```typescript
this.localStorage.getItem<number>('test', { schema: { type: 'number' } })
this.localStorage.getItem('test', { schema: { type: 'number' } })
```

### String

```typescript
this.localStorage.getItem<string>('test', { schema: { type: 'string' } })
this.localStorage.getItem('test', { schema: { type: 'string' } })
```

### Arrays

```typescript
this.localStorage.getItem<boolean[]>('test', { schema: {
this.localStorage.getItem('test', { schema: {
type: 'array',
items: { type: 'boolean' }
} })
```

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

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

```typescript
this.localStorage.getItem<string[]>('test', { schema: {
this.localStorage.getItem('test', { schema: {
type: 'array',
items: { type: 'string' }
} })
Expand Down Expand Up @@ -107,7 +112,7 @@ this.localStorage.getItem<User>('test', { schema })

What's expected for each property is another JSON schema.

## Why a schema *and* a cast?
### Why a schema *and* a cast?

You may ask why we have to define a TypeScript cast with `getItem<User>()` *and* a JSON schema with `{ schema }`.

Expand All @@ -117,30 +122,31 @@ It's because they happen at different steps:

So they each serve a different purpose:
- casting allow you to retrieve the data if the good type instead of `any`
- the schema allow the lib to validate the data at runtime
- the schema allow the lib to validate the data at

For previous basic types, as they are static, we can infer automatically.
But as objects properties are dynamic, we can't do the same for objects.

Be aware **you are responsible the casted type (`User`) describes the same structure as the JSON schema**.
The lib can't check that.

## How to validate fixed values

Temporarily, documentation for constants and enums is removed,
as there will be a breaking change in v8.

## Additional validation

Some types have additional validation options since version >= 6.
### Options for booleans

- `const`

### Options for integers and numbers

- `const`
- `enum`
- `multipleOf`
- `maximum`
- `exclusiveMaximum`
- `minimum`
- `exclusiveMinimum`

For example:

```typescript
this.localStorage.getItem('test', { schema: {
type: 'number',
Expand All @@ -150,6 +156,8 @@ this.localStorage.getItem('test', { schema: {

### Options for strings

- `const`
- `enum`
- `maxLength`
- `minLength`
- `pattern`
Expand Down Expand Up @@ -210,7 +218,7 @@ this.localStorage.getItem<User[]>('test', { schema })
If validation fails, it'll go in the error callback:

```typescript
this.localStorage.getItem<string>('existing', { schema: { type: 'string' } })
this.localStorage.getItem('existing', { schema: { type: 'string' } })
.subscribe((result) => {
// Called if data is valid or null
}, (error) => {
Expand All @@ -221,7 +229,7 @@ this.localStorage.getItem<string>('existing', { schema: { type: 'string' } })
But as usual (like when you do a database request), not finding an item is not an error. It succeeds but returns `null`.

```typescript
this.localStorage.getItem<string>('notExisting', { schema: { type: 'string' } })
this.localStorage.getItem('notExisting', { schema: { type: 'string' } })
.subscribe((result) => {
result; // null
}, (error) => {
Expand Down
Loading