Schema utilities to mirror TypeScript's utility types
What version of Ajv you are you using? 8.11
What problem do you want to solve?
I'm using Ajv as a runtime companion for TypeScript / to generate type guards for my types.
TypeScript has a series of utility types which make it easy to perform certain modifications to existing types without having to re-define the base type. For example I might Omit an id attribute in the context of a create method.
Currently I need to re-define the entire corresponding Ajv schema in these instances if I want a type guard for the new "utilitied" type. This is an issue because it violates DRY in ways that TypeScript has prevented -- if I were to modify the base type / schema I now need to remember to update the contextual schema manually.
It would be wonderful to have a mirrored set of utility functions within the ajv ecosystem which would take a schema and return a modified schema. For example:
interface Foo {
id: number;
bar: string;
}
const fooSchema: JSONSchemaType<Foo> = {
type: 'object',
properties: {
id: { type: 'integer' },
bar: { type: 'string' },
},
required: [ 'id', 'bar' ],
};
// This next schema might be used in the context of a POST /foo endpoint
const modifiedFooSchema: JSONSchemaType<Omit <Foo, 'id'>> = ajv.utilities.omit(
fooSchema,
[ 'id' ]
);
What do you think is the correct solution to problem?
I imagine this would NOT make sense to be built into core ajv, but maybe this would be appropriate as an ajv plugin.
Will you be able to implement it?
If this fits into the vision of the Ajv team and I have some high level guidance about the best way to implement it (e.g. "this should be a plugin!"), I could create an implementation.
The problem with this approach is it obscures the fact that the validation logic deals with the immutable serializable concrete structures, not a subset of abstract javascript/typescript types. Basically for every new schema object a completely new validation function has to be compiled. This process becomes more expensive with more functions and the size of the schema, requiring an async logic for this whole process past certain threshold. So you have two ways about it:
- Create a separate schema for each permutation you need and compile validation function for it. Yes it is annoying, but this is how json schema works and you shouldn't obscure this fact in the repo code.
$defskeyword might help structuring the ever increasing amount of schema files, but can hurt readability if not used properly. - Create an abstract class which implements the interface for the resulting schema and add methods which transform into needed permutations. This way typescript will error on any type mismatch or missing implementations on a concrete class.
In general avoid creating schemas for the structures which aren't going to be serialized. It is tempting to do because the simple JSON schemas map neatly into simple typescript interfaces, but the more complex logic on either of those gets increasingly hard to maintain and down the line makes validation logic incomprehensible.