from-schema
Version:
Infer TypeScript types from JSON schemas
646 lines (485 loc) • 15.3 kB
Markdown
# from-schema
## Convert JSON schemas to TypeScript types
### Notes
- Only a small subset of JSONschema is supported
- This library is very opinionated
- Probably don't use this in production
### Example - social media post
```tsx
// Import various from-schema types
import { ArraySchema, EnumSchema, FromSchema, ObjectSchema } from 'from-schema';
// Import project-specific models defined elsewhere
import { attachment } from './attachment';
import { postBody } from './postBody';
import { postTitle } from './postTitle';
import { user } from './user';
// uuid can be passed to AJV for full UUID validation
export const uuid = {
type: 'string',
pattern:
'^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}$',
} as const satisfies StringSchema;
// TypeScript will see Uuid as a standard string
export type Uuid = FromSchema<typeof uuid>;
/** Equivalent:
* type Uuid = string;
* */
// Define an EnumSchema for the post body type
export const bodyType = {
enum: ['plaintext', 'markdown'],
} as const satisfies EnumSchema;
// Export a type inferred from bodyType
export type BodyType = FromSchema<typeof bodyType>;
/** Equivalent:
* type BodyType = 'plaintext' | 'markdown'
* */
// Define an array of attachments (defined elsewhere)
export const attachments = {
type: 'array',
items: attachment,
} as const satisfies ArraySchema;
// Infer a type based on that model
export type Attachments = FromSchema<typeof attachments>;
/* Equivalent:
* type Attachments = {<external definition>}[];
* */
// Define the post itself -- AJV can compile this into a validator
export const post = {
description: 'A post containing text, images, or videos',
type: 'object',
properties: {
id: uuid, // You can embed schemas in other schemas
body: postBody, // Maybe this is a string with specific constraints?
bodyType,
groupId: uuid,
attachments,
authorId: user.properties.id, // You can model document relationships
likes: {
type: 'number',
},
publishDate: {
type: 'string',
},
replies: {
type: 'number',
},
title: postTitle, // Another string with constraints defined elsewhere
thread: {
type: 'array',
items: uuid,
},
},
required: [
'id',
'authorId',
'attachments',
'publishDate',
'likes',
'replies',
],
} as const satisfies ObjectSchema;
// Infer a TypeScript type
export type Post = FromSchema<typeof post>;
/** Equivalent:
* type Post = {
* id: string,
* body?: string,
* bodyType?: 'plaintest' | 'markdown'
* groupId?: string,
* attachments: {...}[],
* authorId: string,
* likes: number,
* publishDate: string,
* replies: number,
* title?: string,
* thread?: string[]
* }
* */
```
## Generated documentation
This section is experimental and may be removed in future.
### ArraySchema
Defined in `ArraySchema.ts`.
```typescript
export type ArraySchema = SchemaBase & {
readonly type: 'array';
readonly items: SchemaOrPrimitive;
readonly maxItems?: number;
readonly minItems?: number;
readonly uniqueItems?: boolean;
};
```
Represents an array schema. It extends the `SchemaBase` and has the following properties:
- `type`: Must be `'array'`.
- `items`: The schema or primitive type for the array elements.
- `maxItems` (optional): The maximum length of the array.
- `minItems` (optional): The minimum length of the array.
- `uniqueItems` (optional): Whether the array must contain unique items.
### BooleanSchema
Defined in `BooleanSchema.ts`.
```typescript
export type BooleanSchema = SchemaBase & {
readonly type: 'boolean';
};
```
Represents a boolean schema. It extends the `SchemaBase` and has the following property:
- `type`: Must be `'boolean'`.
### EnumSchemaOf
Defined in `EnumSchemaOf.ts`.
```typescript
export type EnumSchemaOf<K> = SchemaBase & {
readonly enum: readonly K[];
};
```
Represents an enumeration schema of a specific type `K`. It extends the `SchemaBase` and has the following property:
- `enum`: An array of values of type `K` that the schema can take.
### EnumSchema
Defined in `EnumSchema.ts`.
```typescript
export type EnumSchema = SchemaBase & {
readonly enum: readonly (string | number | StringSchema | NumberSchema)[];
};
```
Represents an enumeration schema. It extends the `SchemaBase` and has the following property:
- `enum`: An array of string, number, `StringSchema`, or `NumberSchema` values that the schema can take.
### FromObjectSchema
Defined in `FromObjectSchema.ts`.
```typescript
export type FromObjectSchema<T extends ObjectSchema> = OnlyRequired<T> &
OnlyOptional<T>;
```
A utility type that constructs an object type from an `ObjectSchema`. It combines the required and optional properties of the schema.
### FromPropertySchemas
Defined in `FromPropertySchemas.ts`.
```typescript
export type FromPropertySchemas<T extends ObjectSchema> = {
-readonly [K in keyof T['properties']]: PrimitiveOrFromSchema<
T['properties'][K]
>;
};
```
A utility type that constructs an object type from the `properties` of an `ObjectSchema`. It maps each property to its primitive or schema type.
### FromSchema
Defined in `FromSchema.ts`.
```typescript
export type FromSchema<T> = T extends StringSchema
? string
: T extends NumberSchema
? number
: T extends BooleanSchema
? boolean
: T extends UnionSchemaOf<infer P extends SchemaOrPrimitive>
? PrimitiveOrFromSchema<P>
: T extends EnumSchemaOf<infer P extends SchemaOrPrimitive>
? PrimitiveOrFromSchema<P>
: T extends ObjectSchema
? FromObjectSchema<T>
: T extends MapSchema
? FromMapSchema<T>
: T extends ArraySchema
? FromSchema<T['items']>[]
: NoSchema;
```
A utility type that constructs a TypeScript type from a schema type. It recursively maps the schema types to their corresponding TypeScript types.
### HttpRoute
Defined in `HttpRoute.ts`.
```typescript
export type HttpRoute = SchemaBase & {
readonly request: ObjectSchema;
readonly response: ObjectSchema;
readonly authenticated: boolean;
};
```
Represents an HTTP route schema. It extends the `SchemaBase` and has the following properties:
- `request`: An `ObjectSchema` representing the request schema.
- `response`: An `ObjectSchema` representing the response schema.
- `authenticated`: A boolean indicating if authentication is required for the route.
### MapSchema
Defined in `MapSchema.ts`.
```typescript
export type MapSchema = {
readonly type: 'map';
readonly keys: StringSchema | EnumSchemaOf<string>;
readonly values: Schema;
readonly partial?: boolean;
};
```
Represents a map schema. It has the following properties:
- `type`: Must be `'map'`.
- `keys`: A `StringSchema` or `EnumSchemaOf<string>` representing the schema for the map keys.
- `values`: The schema for the map values.
- `partial` (optional): A boolean indicating if the map is partial (allows undefined values).
### MapSchemaOf
Defined in `MapSchema.ts`.
```typescript
export type MapSchemaOf<
K extends StringSchema | EnumSchemaOf<string>,
V,
P = false,
> = SchemaBase & {
readonly type: 'map';
readonly keys: K;
readonly values: V;
readonly partial: P;
};
```
Represents a map schema of specific key and value types. It extends the `SchemaBase` and has the following properties:
- `type`: Must be `'map'`.
- `keys`: The schema for the map keys, of type `K`.
- `values`: The schema or primitive type for the map values, of type `V`.
- `partial`: A boolean indicating if the map is partial (allows undefined values), defaulting to `false`.
### FromMapSchema
Defined in `MapSchema.ts`.
```typescript
export type FromMapSchema<S extends MapSchema> = S['partial'] extends true
? {
-readonly [key in S['keys'] extends EnumSchemaOf<infer M extends string>
? M
: string]?: FromSchema<S['values']>;
}
: {
-readonly [key in S['keys'] extends EnumSchemaOf<infer M extends string>
? M
: string]: FromSchema<S['values']>;
};
```
A utility type that constructs a map type from a `MapSchema`. It maps the key and value schemas to their corresponding TypeScript types, considering the `partial` property.
### NumberSchema
Defined in `NumberSchema.ts`.
```typescript
export type NumberSchema = SchemaBase & {
readonly type: 'number' | 'integer';
readonly minimum?: number;
readonly maximum?: number;
};
```
Represents a number schema. It extends the `SchemaBase` and has the following properties:
- `type`: Must be `'number'` or `'integer'`.
- `minimum` (optional): The minimum value for the number.
- `maximum` (optional): The maximum value for the number.
### ObjectSchema
Defined in `ObjectSchema.ts`.
```typescript
export type ObjectSchemaWithoutRequired = SchemaBase & {
readonly type: 'object';
readonly properties: Record<string, SchemaOrPrimitive>;
readonly minProperties?: number;
readonly maxProperties?: number;
};
export type ObjectSchemaWithRequired = ObjectSchemaWithoutRequired & {
readonly required: readonly string[];
};
export type ObjectSchema =
| ObjectSchemaWithoutRequired
| ObjectSchemaWithRequired;
```
Represents an object schema. It extends the `SchemaBase` and has the following properties:
- `type`: Must be `'object'`.
- `properties`: A record of property names and their corresponding schemas or primitive types.
- `minProperties` (optional): The minimum number of properties required in the object.
- `maxProperties` (optional): The maximum number of properties allowed in the object.
- `required` (optional): An array of required property names.
### OnlyOptional
Defined in `OnlyOptional.ts`.
```typescript
export type OnlyOptional<T extends ObjectSchema> =
T extends ObjectSchemaWithRequired
? Partial<Omit<FromPropertySchemas<T>, RequiredPropOf<T>>>
: Partial<T['properties']>;
```
A utility type that constructs an object type with only the optional properties of an `ObjectSchema`.
### OnlyRequired
Defined in `OnlyRequired.ts`.
```typescript
export type OnlyRequired<T extends ObjectSchema> =
T extends ObjectSchemaWithRequired
? Required<Pick<FromPropertySchemas<T>, RequiredPropOf<T>>>
: Record<string, never>;
```
A utility type that constructs an object type with only the required properties of an `ObjectSchema`.
### PrimitiveOrFromSchema
Defined in `PrimitiveOrFromSchema.ts`.
```typescript
export type PrimitiveOrFromSchema<T extends SchemaOrPrimitive> =
T extends Schema ? FromSchema<T> : T;
```
A utility type that constructs a TypeScript type from a schema or primitive type. If the input is a schema, it uses `FromSchema` to convert it to a TypeScript type; otherwise, it returns the primitive type as is.
### Primitive
Defined in `Primitive.ts`.
```typescript
export type Primitive = string | number | boolean | string[];
```
Represents a primitive type, which can be a string, number, boolean, or an array of strings.
### RequiredPropOf
Defined in `RequiredPropOf.ts`.
```typescript
export type RequiredPropOf<T extends ObjectSchemaWithRequired> = Extract<
keyof T['properties'],
T['required'][number]
>;
```
A utility type that extracts the required property names from an `ObjectSchemaWithRequired`.
### SchemaBase
Defined in `SchemaBase.ts`.
```typescript
export type SchemaBase = {
readonly description?: string;
};
```
Represents the base schema type. It has an optional `description` property for providing a description of the schema.
### SchemaOrPrimitive
Defined in `SchemaOrPrimitive.ts`.
```typescript
export type SchemaOrPrimitive = Schema | Primitive;
```
Represents a schema or primitive type.
### Schema
Defined in `Schema.ts`.
```typescript
export type Schema =
| ArraySchema
| BooleanSchema
| EnumSchema
| MapSchema
| NumberSchema
| ObjectSchema
| StringSchema
| UnionSchema;
```
A union type representing all the available schema types.
### StringSchema
Defined in `StringSchema.ts`.
```typescript
export type StringSchema = SchemaBase & {
readonly type: 'string';
readonly minLength?: number;
readonly maxLength?: number;
readonly pattern?: string;
readonly format?: string;
};
```
Represents a string schema. It extends the `SchemaBase` and has the following properties:
- `type`: Must be `'string'`.
- `minLength` (optional): The minimum length of the string.
- `maxLength` (optional): The maximum length of the string.
- `pattern` (optional): A regular expression pattern that the string must match.
- `format` (optional): A format specifier for the string (e.g., 'date', 'email').
### UnionSchemaOf
Defined in `UnionSchemaOf.ts`.
```typescript
export type UnionSchemaOf<K extends SchemaOrPrimitive> = SchemaBase & {
readonly union: readonly K[];
};
```
Represents a union schema of a specific type `K`. It extends the `SchemaBase` and has the following property:
- `union`: An array of schemas or primitive types that the schema can take.
### UnionSchema
Defined in `UnionSchema.ts`.
```typescript
export type UnionSchema = SchemaBase & {
readonly union: readonly SchemaOrPrimitive[];
};
```
Represents a union schema. It extends the `SchemaBase` and has the following property:
- `union`: An array of schemas or primitive types that the schema can take.
### WebSocketRoute
Defined in `WebSocketRoute.ts`.
```typescript
export type WebSocketRoute = SchemaBase & Record<string, never>;
```
Represents a WebSocket route schema. It extends the `SchemaBase` and has no additional properties.
## Usage Examples
Here are some usage examples for the different schema types:
### ArraySchema
```typescript
import { ArraySchema } from './ArraySchema';
const myArraySchema: ArraySchema = {
type: 'array',
items: {
type: 'string',
},
maxItems: 10,
minItems: 1,
};
```
### BooleanSchema
```typescript
import { BooleanSchema } from './BooleanSchema';
const myBooleanSchema: BooleanSchema = {
type: 'boolean',
};
```
### EnumSchema
```typescript
import { EnumSchema } from './EnumSchema';
const myEnumSchema: EnumSchema = {
enum: ['red', 'green', 'blue'],
};
```
### MapSchema
```typescript
import { MapSchema } from './MapSchema';
const myMapSchema: MapSchema = {
type: 'map',
keys: {
type: 'string',
},
values: {
type: 'number',
},
partial: true,
};
```
### NumberSchema
```typescript
import { NumberSchema } from './NumberSchema';
const myNumberSchema: NumberSchema = {
type: 'number',
minimum: 0,
maximum: 100,
};
```
### ObjectSchema
```typescript
import { ObjectSchema } from './ObjectSchema';
const myObjectSchema: ObjectSchema = {
type: 'object',
properties: {
name: {
type: 'string',
},
age: {
type: 'number',
},
},
required: ['name'],
};
```
### StringSchema
```typescript
import { StringSchema } from './StringSchema';
const myStringSchema: StringSchema = {
type: 'string',
minLength: 1,
maxLength: 50,
pattern: '^[A-Za-z]+$',
};
```
### UnionSchema
```typescript
import { UnionSchema } from './UnionSchema';
const myUnionSchema: UnionSchema = {
union: [
{
type: 'string',
},
{
type: 'number',
},
],
};
```
## Contribution
Contributions to this project are welcome. If you find any issues or have suggestions for improvements, please open an issue or submit a pull request on the project's repository.
## License
This project is licensed under the [MIT License](LICENSE).