Juniper round trip
Juniper builds a JSON Schema and its type together. Once the schema is serialized, to a file or into an OpenAPI spec, only the JSON is left. genever reads that JSON back into the type Juniper inferred:
ts
import { arraySchema, objectSchema, stringSchema } from 'juniper';
const user = objectSchema({
properties: {
id: stringSchema().startsWith('usr_'),
tags: arraySchema(stringSchema()).minItems(1),
},
required: ['id'],
});
// Written to user.json, then `genever user.json --name User` writes:
// export type User = {
// id: `usr_${string}`;
// tags?: [string, ...string[]];
// };genever's tests build schemas covering every Juniper schema type and keyword, serialize them to JSON text, genever them, and check the generated type is equivalent to Juniper's SchemaType, in both JSON Schema 2020-12 and OpenAPI 3.0. Juniper's own escape hatch, customSchema<T>(), takes a hand-written type, which is the type genever saves you from maintaining.
Differences
genever reads only the JSON. Where the JSON says more than Juniper's type does, or less, genever follows the JSON:
- An object schema with no properties is
Record<string, unknown>, not Juniper's empty object type. - Prefix items beyond
minItemsare optional, as a validator accepts a shorter array. Juniper'stupleSchema()always setsminItems, so its tuples are identical. - In
if/then/else, a property set tofalseinelseis?: never. - A type that only exists in TypeScript, from
.cast<T>()orcustomSchema<T>(), is read from the JSON the schema emits instead. minItemsis typed up to 3 by default, as in Juniper. genever's--typed-min-itemschanges the cap.