Skip to content

Commit

Permalink
fix type inference for JTDSchemaType in compileParser/Serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
epoberezkin committed Mar 7, 2021
1 parent 23293be commit db553ad
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 6 deletions.
4 changes: 2 additions & 2 deletions docs/guide/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ const schema = {

type MyData = JTDDataType<typeof schema>

// validate is a type guard for MyData - type is inferred from schema type
const validate = ajv.compile(schema)
// type inference is not supported for JTDDataType yet
const validate = ajv.compile<MyData>(schema)

const validData = {
foo: 1,
Expand Down
7 changes: 5 additions & 2 deletions lib/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ export default class Ajv {
validate(schema: Schema | string, data: unknown): boolean
validate(schemaKeyRef: AnySchema | string, data: unknown): boolean | Promise<unknown>
validate<T>(schema: Schema | JSONSchemaType<T> | string, data: unknown): data is T
// This is separated to help typescript with inference
// Separated for type inference to work
// eslint-disable-next-line @typescript-eslint/unified-signatures
validate<T>(schema: JTDSchemaType<T>, data: unknown): data is T
validate<T>(schema: AsyncSchema, data: unknown | T): Promise<T>
Expand All @@ -324,7 +324,7 @@ export default class Ajv {
// Create validation function for passed schema
// _meta: true if schema is a meta-schema. Used internally to compile meta schemas of user-defined keywords.
compile<T = unknown>(schema: Schema | JSONSchemaType<T>, _meta?: boolean): ValidateFunction<T>
// This is separated to help typescript with inference
// Separated for type inference to work
// eslint-disable-next-line @typescript-eslint/unified-signatures
compile<T = unknown>(schema: JTDSchemaType<T>, _meta?: boolean): ValidateFunction<T>
compile<T = unknown>(schema: AsyncSchema, _meta?: boolean): AsyncValidateFunction<T>
Expand All @@ -342,6 +342,9 @@ export default class Ajv {
schema: SchemaObject | JSONSchemaType<T>,
_meta?: boolean
): Promise<ValidateFunction<T>>
// Separated for type inference to work
// eslint-disable-next-line @typescript-eslint/unified-signatures
compileAsync<T = unknown>(schema: JTDSchemaType<T>, _meta?: boolean): Promise<ValidateFunction<T>>
compileAsync<T = unknown>(schema: AsyncSchema, meta?: boolean): Promise<AsyncValidateFunction<T>>
// eslint-disable-next-line @typescript-eslint/unified-signatures
compileAsync<T = unknown>(
Expand Down
12 changes: 10 additions & 2 deletions lib/jtd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,20 @@ export default class Ajv extends AjvCore {
super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : undefined))
}

compileSerializer<T = unknown>(schema: SchemaObject | JTDSchemaType<T>): (data: T) => string {
compileSerializer<T = unknown>(schema: SchemaObject): (data: T) => string
// Separated for type inference to work
// eslint-disable-next-line @typescript-eslint/unified-signatures
compileSerializer<T = unknown>(schema: JTDSchemaType<T>): (data: T) => string
compileSerializer<T = unknown>(schema: SchemaObject): (data: T) => string {
const sch = this._addSchema(schema)
return sch.serialize || this._compileSerializer(sch)
}

compileParser<T = unknown>(schema: SchemaObject | JTDSchemaType<T>): JTDParser<T> {
compileParser<T = unknown>(schema: SchemaObject): JTDParser<T>
// Separated for type inference to work
// eslint-disable-next-line @typescript-eslint/unified-signatures
compileParser<T = unknown>(schema: JTDSchemaType<T>): JTDParser<T>
compileParser<T = unknown>(schema: SchemaObject): JTDParser<T> {
const sch = this._addSchema(schema)
return (sch.parse || this._compileParser(sch)) as JTDParser<T>
}
Expand Down
46 changes: 46 additions & 0 deletions spec/types/jtd-schema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,27 @@ describe("JTDSchemaType", () => {
should.not.exist(ajv.errors)
})

it("parser should return correct data type", () => {
const ajv = new _Ajv()
const parse = ajv.compileParser(mySchema)
const validJson = '{"type": "a", "a": 1}'
const data = parse(validJson)
if (data !== undefined && data.type === "a") {
data.a.should.equal(1)
}
should.not.exist(parse.message)
})

it("serializer should only accept correct data type", () => {
const ajv = new _Ajv()
const serialize = ajv.compileSerializer(mySchema)
const validData = {type: "a" as const, a: 1}
serialize(validData).should.equal('{"type":"a","a":1}')
const invalidData = {type: "a" as const, b: "test"}
// @ts-expect-error
serialize(invalidData)
})

it("should typecheck number schemas", () => {
const numf: JTDSchemaType<number> = {type: "float64"}
const numi: JTDSchemaType<number> = {type: "int32"}
Expand Down Expand Up @@ -322,6 +343,31 @@ describe("JTDSchemaType", () => {
})

describe("JTDDataType", () => {
it("validation should prove the data type", () => {
const ajv = new _Ajv()
const mySchema1 = {
discriminator: "type",
mapping: {
a: {properties: {a: {type: "float64"}}},
b: {optionalProperties: {b: {type: "string"}}},
},
} as const

type MyData1 = JTDDataType<typeof mySchema1>

const validate = ajv.compile<MyData1>(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") {
validData.a.should.equal(1)
}
should.not.exist(ajv.errors)
})

it("should typecheck number schemas", () => {
const numSchema = {type: "float64"} as const
const num: TypeEquality<JTDDataType<typeof numSchema>, number> = true
Expand Down

0 comments on commit db553ad

Please sign in to comment.