json-schema-to-typescript icon indicating copy to clipboard operation
json-schema-to-typescript copied to clipboard

Feature request: Runtime validation

Open chriskuech opened this issue 6 years ago • 4 comments

For a schema "MyType", I would like this tool to generate the following TypeScript code.

import Ajv from "ajv";
const ajv = new Ajv();

const MyTypeSchema = {...}; // the schema object
const validateMyType = ajv.compile(MyTypeSchema);

export interface MyType {
    ...
}

export function MyType(value: any): MyType {
    if (validateMyType(value)) {
        return value as MyType;
    } else {
        throw new TypeError("Cannot convert value to type 'MyType'");
    }
}

This would be invaluable addition, because currently TypeScript's greatest weakness is no dynamic type checking for I/O scenarios.

I would be willing to PR this if it is very likely to be accepted.

chriskuech avatar Jun 15 '19 05:06 chriskuech

This is a neat idea, but my hunch is it'll be pretty hard to do in practice. Do you want to flush out (in this issue, or with code) what the runtime validation code might look like, and what the edge cases we should watch out for are? I'd encourage you to think exhaustively about the set of types we need to handle: arrays, tuples, objects, referenced types, custom types, and so on.

bcherny avatar Jun 15 '19 20:06 bcherny

I don't think this would be that difficult.

We would add a type conversion error class to the main module

export class TypeConversionError extends TypeError {
    constructor(public readonly typeName: string, public readonly issues: any) {
        super(`Failed to convert value to '${typeName}'`);
    }
}

At the top of each file, we would generate

import Ajv from "ajv";
const ajv = new Ajv();

The for each type (ex: MyType)

  1. Generate the schema as an value (ie the JSON.parse(rawSchemaString))
    const MyTypeSchema = {...}; // the schema object
    
  2. Generate the type validation function leveraging the schema
    const validateMyType = ajv.compile(MyTypeSchema);
    
  3. Generate the type (the existing behavior of this tool)
    export type MyType = ...;
    
  4. Generate the type safe casting function
    export function MyType(value: any): MyType {
        const valid = validateMyType(value);
        if (valid) {
            return value as MyType;
        } else {
            throw new TypeConversionError("MyType", ajv.errors);
        }
    }
    

Do you have concerns about any specific step of this?

chriskuech avatar Jun 15 '19 20:06 chriskuech

Looks like the biggest issue is generating the schema object. Currently, the pipeline only passes through the AST, whereas we would have to pass the schemas alongside the AST through the pipeline.

chriskuech avatar Jun 17 '19 03:06 chriskuech

apologies for the dupe. i swore i searched. don't know how i missed that, thanks.

Edit: oops. i meant to post this in my issue...

but my hunch is it'll be pretty hard to do in practice

you've already done the hard part (generating ts types from a json schema). as for validation, ajv does all the heavy lifting there. as long as the ts type is accurate to the json schema it should be good to go, no?

speaking for myself, what i'm after is something pretty simple: just generating the ajv boilerplate alongside the ts type as a single unit. that way it's impossible for the two to diverge or break in subtle ways.

cmawhorter avatar Nov 30 '20 01:11 cmawhorter