Skip to content

Commit

Permalink
Merge pull request #1547 from erikbrinkman/jtd-type
Browse files Browse the repository at this point in the history
Add JTDDataType to compile signature
  • Loading branch information
epoberezkin authored Apr 15, 2021
2 parents 8762e6f + 0d5c18c commit baf1475
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 16 deletions.
16 changes: 14 additions & 2 deletions lib/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export {KeywordCxt} from "./compile/validate"
export {DefinedError} from "./vocabularies/errors"
export {JSONType} from "./compile/rules"
export {JSONSchemaType} from "./types/json-schema"
export {JTDSchemaType} from "./types/jtd-schema"
export {JTDSchemaType, SomeJTDSchemaType, JTDDataType} from "./types/jtd-schema"
export {_, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions} from "./compile/codegen"

import type {
Expand All @@ -50,7 +50,7 @@ import type {
AddedFormat,
} from "./types"
import type {JSONSchemaType} from "./types/json-schema"
import type {JTDSchemaType} from "./types/jtd-schema"
import type {JTDSchemaType, SomeJTDSchemaType, JTDDataType} from "./types/jtd-schema"
import ValidationError from "./runtime/validation_error"
import MissingRefError from "./compile/ref_error"
import {getRules, ValidationRules, Rule, RuleGroup, JSONType} from "./compile/rules"
Expand Down Expand Up @@ -313,6 +313,12 @@ export default class Ajv {
// Separated for type inference to work
// eslint-disable-next-line @typescript-eslint/unified-signatures
validate<T>(schema: JTDSchemaType<T>, data: unknown): data is T
// This overload is only intended for typescript inference, the first
// argument prevents manual type annotation from matching this overload
validate<N extends never, T extends SomeJTDSchemaType>(
schema: T,
data: unknown
): data is JTDDataType<T>
validate<T>(schema: AsyncSchema, data: unknown | T): Promise<T>
validate<T>(schemaKeyRef: AnySchema | string, data: unknown): data is T | Promise<T>
validate<T>(
Expand All @@ -338,6 +344,12 @@ export default class Ajv {
// Separated for type inference to work
// eslint-disable-next-line @typescript-eslint/unified-signatures
compile<T = unknown>(schema: JTDSchemaType<T>, _meta?: boolean): ValidateFunction<T>
// This overload is only intended for typescript inference, the first
// argument prevents manual type annotation from matching this overload
compile<N extends never, T extends SomeJTDSchemaType>(
schema: T,
_meta?: boolean
): ValidateFunction<JTDDataType<T>>
compile<T = unknown>(schema: AsyncSchema, _meta?: boolean): AsyncValidateFunction<T>
compile<T = unknown>(schema: AnySchema, _meta?: boolean): AnyValidateFunction<T>
compile<T = unknown>(schema: AnySchema, _meta?: boolean): AnyValidateFunction<T> {
Expand Down
4 changes: 2 additions & 2 deletions lib/jtd.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {AnySchemaObject, SchemaObject, JTDParser} from "./types"
import type {JTDSchemaType, JTDDataType} from "./types/jtd-schema"
import type {JTDSchemaType, SomeJTDSchemaType, JTDDataType} from "./types/jtd-schema"
import AjvCore, {CurrentOptions} from "./core"
import jtdVocabulary from "./vocabularies/jtd"
import jtdMetaSchema from "./refs/jtd-schema"
Expand Down Expand Up @@ -125,5 +125,5 @@ export {KeywordCxt} from "./compile/validate"
export {JTDErrorObject} from "./vocabularies/jtd"
export {_, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions} from "./compile/codegen"

export {JTDSchemaType, JTDDataType}
export {JTDSchemaType, SomeJTDSchemaType, JTDDataType}
export {JTDOptions}
52 changes: 45 additions & 7 deletions lib/types/jtd-schema.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
/** numeric strings */
type NumberType = "float32" | "float64" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32"

/** string strings */
type StringType = "string" | "timestamp"

/** Generic JTD Schema without inference of the represented type */
export type SomeJTDSchemaType = (
| // ref
{ref: string}
// primitives
| {type: NumberType | StringType | "boolean"}
// enum
| {enum: string[]}
// elements
| {elements: SomeJTDSchemaType}
// values
| {values: SomeJTDSchemaType}
// properties
| {
properties: Record<string, SomeJTDSchemaType>
optionalProperties?: Record<string, SomeJTDSchemaType>
additionalProperties?: boolean
}
| {
properties?: Record<string, SomeJTDSchemaType>
optionalProperties: Record<string, SomeJTDSchemaType>
additionalProperties?: boolean
}
// discriminator
| {discriminator: string; mapping: Record<string, SomeJTDSchemaType>}
// empty
// NOTE see the end of
// https://github.com/typescript-eslint/typescript-eslint/issues/2063#issuecomment-675156492
// eslint-disable-next-line @typescript-eslint/ban-types
| {}
) & {
nullable?: boolean
metadata?: Record<string, unknown>
definitions?: Record<string, SomeJTDSchemaType>
}

/** required keys of an object, not undefined */
type RequiredKeys<T> = {
[K in keyof T]-?: undefined extends T[K] ? never : K
Expand Down Expand Up @@ -57,12 +99,6 @@ type IsRecord<T, Union extends boolean> = Union extends IsUnion<Exclude<T, null>
: true
: false

/** numeric strings */
type NumberType = "float32" | "float64" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32"

/** string strings */
type StringType = "string" | "timestamp"

/** actual schema */
export type JTDSchemaType<T, D extends Record<string, unknown> = Record<string, never>> = (
| // refs - where null wasn't specified, must match exactly
Expand Down Expand Up @@ -164,7 +200,9 @@ export type JTDSchemaType<T, D extends Record<string, unknown> = Record<string,
type JTDDataDef<S, D extends Record<string, unknown>> =
| (// ref
S extends {ref: string}
? JTDDataDef<D[S["ref"]], D>
? D extends {[K in S["ref"]]: infer V}
? JTDDataDef<V, D>
: never
: // type
S extends {type: NumberType}
? number
Expand Down
18 changes: 13 additions & 5 deletions spec/types/jtd-schema.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-empty-interface,no-void */
import _Ajv from "../ajv_jtd"
import type {JTDSchemaType, JTDDataType} from "../../dist/jtd"
import type {JTDSchemaType, SomeJTDSchemaType, JTDDataType} from "../../dist/jtd"
import chai from "../chai"
const should = chai.should()

Expand Down Expand Up @@ -353,16 +353,14 @@ describe("JTDDataType", () => {
},
} as const

type MyData1 = JTDDataType<typeof mySchema1>

const validate = ajv.compile<MyData1>(mySchema1)
const validate = ajv.compile(mySchema1)
const validData: unknown = {type: "a", a: 1}
if (validate(validData) && validData.type === "a") {
validData.a.should.equal(1)
}
should.not.exist(validate.errors)

if (ajv.validate<MyData1>(mySchema1, validData) && validData.type === "a") {
if (ajv.validate(mySchema1, validData) && validData.type === "a") {
validData.a.should.equal(1)
}
should.not.exist(ajv.errors)
Expand Down Expand Up @@ -492,3 +490,13 @@ describe("JTDDataType", () => {
void [empty]
})
})

describe("SomeJTDSchemaType", () => {
it("should allow setting unknowns", () => {
// This test is basically here to assert that we should be using `{}` in
// SomeJTDSchemaType
const schema: SomeJTDSchemaType = {}

void [schema]
})
})

0 comments on commit baf1475

Please sign in to comment.