Skip to content

Make versus predicate validation style #3

@bwestergard

Description

@bwestergard

Great work!

Yesterday, in a discussion with a coworker (@jcready), I mused that what we really needed was a way to generate validation functions and Flow definitions from JSON Schemas. I did some half-hearted googling, not expecting to find anything, and discovered your library, which very nearly does what I'd like it to.

A modest proposal

That said, I had in mind a slightly different style of validation. Consider:

// Predicate style
const isString = (x: mixed): boolean => typeof x === 'string'
  ? 0 // No Error
  : 1 // Error
// Make style
const makeString = (x: mixed): string | null => typeof x === 'string'
  ? x // This value is a string due to explicit type check.
  : null // If a value inhabiting `string`, or more generally some type `T` defined by the JSON schema, cannot be constructed, return null.

You are currently generating "predicate style" validators. If you're amenable to it, I'd like to extend this library to support "make style" because:

  1. Make style, if generated with annotations, can be checked for correctness by Flow. If generated without annotations, one can inspect the inferred type to ensure it accords with one's intent in writing the JSON schema. One need not convince onself of json-schema-validator-generator's correctness if one is convinced of Flow's correctness. Bugs in json-schema-validator-generator at worst produce "make" functions that returns the wrong values inhabiting the type (e.g. makeString could return "lol" for all inputs, or reject everything with null). This makes refactoring after a change in JSONSchema (and ensuing changes in Flow type declarations and validation functions) less error prone (declarations and functions cannot get out of sync without throwing errors).
  2. Make style can be extended to provide useful error messages by swapping nullable return types for Result types (cf. Rust).

Example

To illustrate No. 2, consider this simple JSONSchema.

{
    "title": "Person",
    "type": "object",
    "properties": {
        "firstName": {
            "type": "string"
        },
        "lastName": {
            "type": "string"
        }
    },
    "required": ["firstName", "lastName"],
    "additionalProperties": false
}

For which we'd generate a make style validator resembling:

type Result<O,F> =
| {
  +result: 'ok',
  value: O
}
| {
  +result: 'fail',
  value: F
}

type Person = {
  firstName: string,
  lastName: string
}

type Explanation = string

const makePerson = (x: mixed): Result<Person, Explanation> => {
  if (
    x !== null &&
    typeof x === 'object'
  ) {
    if ('firstName' in x) {
      if (typeof x.firstName === 'string') {
        if ('lastName' in x) {
          if (typeof x.lastName === 'string') {
            return {
              result: 'ok',
              value: {
                firstName: x.firstName,
                lastName: x.lastName
              }
            }
          } else {
            return {
              result: 'fail',
              value: 'lastName is not string.'
            }
          }
        } else {
          return {
            result: 'fail',
            value: 'Missing lastName key.'
          }
        }
      } else {
        return {
          result: 'fail',
          value: 'firstName is not string.'
        }
      }
    } else {
      return {
        result: 'fail',
        value: 'Missing firstName key.'
      }
    }
  } else {
    return {
      result: 'fail',
      value: 'Not an object.'
    }
  }
}

See on Try Flow. Handling errors in arbitrarily deeply nested JSON structures could be done with an Explanation type that included a JSONPath. The JSON Schema's metadata (e.g. description fields) can be incorporated in the validation error messages.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions