Skip to content
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

Documentation for JTDSchemaType #1457

Merged
merged 4 commits into from
Feb 23, 2021
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
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ if (validate(data)) {

With JSON Type Definition schema:

In JavaScript:

```javascript
const Ajv = require("ajv").default

Expand All @@ -292,6 +294,22 @@ const valid2 = validate({}) // false, foo is required
const valid3 = validate({foo: 1, bar: 2}) // false, bar is additional
```

In TypeScript:

```typescript
import {JTDSchemaType} from "ajv"

type MyData = {foo: number}

// Optional schema type annotation for schema to match MyData type.
// To use JTDSchemaType set `strictNullChecks: true` in tsconfig `compilerOptions`.
const schema: JTDSchemaType<MyData> = {
properties: {
foo: {type: "float64"},
},
}
```

See [this test](./spec/types/json-schema.spec.ts) for an advanced example, [API reference](./docs/api.md) and [Options](./docs/api.md#options) for more details.

Ajv compiles schemas to functions and caches them in all cases (using schema itself as a key for Map) or another function passed via options), so that the next time the same schema is used (not necessarily the same object instance) it won't be compiled again.
Expand Down
68 changes: 68 additions & 0 deletions docs/json-type-definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const ajv = new AjvJTD()
- [values](#values-schema-form) (for dictionary)
- [ref](#ref-schema-form) (to reference a schema in definitions)
- [empty](#empty-schema-form) (for any data)
- [JTDSchemaType](#jtdschematype)
- [Extending JTD](#extending-jtd)
- [metadata](#metadata-schema-member)
- [union](#union-keyword)
Expand Down Expand Up @@ -313,6 +314,73 @@ Unlike JSON Schema, JTD does not allow to reference:

Empty JTD schema defines the data instance that can be of any type, including JSON `null` (even if `nullable` member is not present). It cannot have any member other than `nullable` and `metadata`.

## JTDSchemaType

The type `JTDSchemaType` can be used to validate that the written schema matches the type you expect to validate. This type is strict such that if typescript compiles, you should require no further type guards. The downside of this is that the types that `JTDSchemaType` can verify are limited to the types that JTD can verify. If a type doesn't verify, `JTDSchemaType` should resolve to `never`, throwing an error when you try to assign to it. This means that types like `1 | 2 | 3`, or general untagged unions (outside of unions of string literals) cannot be used with `JTDSchemaType`.

### Most Schemas

Most straightforward types should work with `JTDSchemaType`, e.g.
```typescript
interface MyType {
num: number;
optionalStr?: string;
nullableEnum: "v1.0" | "v1.2" | null;
values: Record<string, number>;
}

const schema: JTDSchemaType<MyType> = {
properties: {
num: { type: "float64" },
nullableEnum: { enum: ["v1.0", "v1.2"], nullable: true },
values: { values: { type: "int32" } },
},
optionalProperties: {
optionalStr: { type: "string" },
}
}
```
will compile. Using `schema` with AJV will guarantee type safety.

### Ref Schemas

Ref schemas are a little more advanced, because the types of every definition must be specified in advance.
A simple ref schema is relatively straightforward:
```typescript
const schema: JTDSchemaType<{ val: number }, { num: number }> = {
definitions: {
num: { type: "float64" }
},
properties: {
val: { ref: "num" }
},
}
```
note that the type of all definitions was included as a second argument to `JTDSchemaType`.

This also works for recursive schemas:
```typescript
type LinkedList = { val: number, next?: LinkedList }
const schema: JTDSchemaType<LinkedList, { node: LinkedList }> = {
definitions: {
node: {
properties: {
val: { type: "float64" },
},
optionalProperties: {
next: { ref: "node" },
},
},
},
ref: "node",
}
```

### Notable Omissions

`JTDSchemaType` currently validats that if the schema compiles it will verify an accurate type, but there are a few places with potentially unexpected behavior.
`JTDSchemaType` doesn't verify the schema is correct. It won't reject schemas that definitions anywhere by the root, and it won't reject discriminator schemas that still define the descriminator in mapping properties. It also won't verify that enum schemas have every enum member as this isn't generally feasible in typescript yet.

## Extending JTD

### Metadata schema member
Expand Down