@sinclair/typebox
Advanced tools
Comparing version 0.12.9 to 0.14.0
{ | ||
"name": "@sinclair/typebox", | ||
"version": "0.12.9", | ||
"version": "0.14.0", | ||
"description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", | ||
@@ -22,2 +22,3 @@ "keywords": [ | ||
"build": "smoke-task build", | ||
"example": "smoke-task example", | ||
"test": "smoke-task spec" | ||
@@ -33,2 +34,3 @@ }, | ||
"mocha": "^7.1.1", | ||
"smoke-run": "^1.1.2", | ||
"smoke-task": "^1.1.2", | ||
@@ -35,0 +37,0 @@ "typescript": "^4.1.2", |
@@ -47,2 +47,3 @@ <div align='center'> | ||
- [Modifiers](#Modifiers) | ||
- [Utility Types](#Utility-Types) | ||
- [Options](#Options) | ||
@@ -257,3 +258,2 @@ - [Strict](#Strict) | ||
``` | ||
<a name="Modifiers"></a> | ||
@@ -301,2 +301,61 @@ | ||
``` | ||
<a name="Utility-Types"></a> | ||
### Utility Types | ||
TypeBox supports a subset of TypeScripts built-in [Utility Types](https://www.typescriptlang.org/docs/handbook/utility-types.html). These types operate on schemas of `Type.Object({...})` only. The following table outlines the TypeBox mappings between TypeScript and JSON schema. | ||
```typescript | ||
┌────────────────────────────────┬─────────────────────────────┬─────────────────────────────┐ | ||
│ const T = Type.Partial( │ type T = Partial<{ │ const T = { │ | ||
│ Type.Object({ │ x: number, │ type: 'object', │ | ||
│ x: Type.Number(), │ y: number │ properties: { │ | ||
│ y: Type.Number() | }> │ x: { │ | ||
│ }) │ │ type: 'number' │ | ||
│ ) │ │ }, │ | ||
│ │ │ y: { │ | ||
│ │ │ type: 'number' │ | ||
│ │ │ } │ | ||
│ │ │ } │ | ||
│ │ │ } │ | ||
├────────────────────────────────┼─────────────────────────────┼─────────────────────────────┤ | ||
│ const T = Type.Required( │ type T = Required<{ │ const T = { │ | ||
│ Type.Object({ │ x?: number, │ type: 'object', │ | ||
│ x: Type.Optional( │ y?: number │ properties: { │ | ||
│ Type.Number() | }> │ x: { │ | ||
│ ), │ │ type: 'number' │ | ||
│ y: Type.Optional( │ │ }, │ | ||
│ Type.Number() │ │ y: { │ | ||
│ ) │ │ type: 'number' │ | ||
│ }) │ │ } │ | ||
│ ) │ │ } │ | ||
│ │ │ required: ['x', 'y'] │ | ||
│ │ │ } │ | ||
├────────────────────────────────┼─────────────────────────────┼─────────────────────────────┤ | ||
│ const T = Type.Pick( │ type T = Pick<{ │ const T = { │ | ||
│ Type.Object({ │ x: number, │ type: 'object', │ | ||
│ x: Type.Optional( │ y: number │ properties: { │ | ||
│ Type.Number() | }, 'x'> │ x: { │ | ||
│ ), │ │ type: 'number' │ | ||
│ y: Type.Optional( │ │ } │ | ||
│ Type.Number() │ │ }, │ | ||
│ ) │ │ required: ['x'] │ | ||
│ }) │ │ } │ | ||
│ , ['x']) │ │ │ | ||
│ │ │ │ | ||
├────────────────────────────────┼─────────────────────────────┼─────────────────────────────┤ | ||
│ const T = Type.Omit( │ type T = Omit<{ │ const T = { │ | ||
│ Type.Object({ │ x: number, │ type: 'object', │ | ||
│ x: Type.Optional( │ y: number │ properties: { │ | ||
│ Type.Number() | }, 'y'> │ x: { │ | ||
│ ), │ │ type: 'number' │ | ||
│ y: Type.Optional( │ │ } │ | ||
│ Type.Number() │ │ }, │ | ||
│ ) │ │ required: ['x'] │ | ||
│ }) │ │ } │ | ||
│ , ['y']) │ │ │ | ||
│ │ │ │ | ||
└────────────────────────────────┴─────────────────────────────┴─────────────────────────────┘ | ||
``` | ||
<a name="Options"></a> | ||
@@ -303,0 +362,0 @@ |
@@ -164,15 +164,22 @@ export declare const ReadonlyOptionalModifier: unique symbol; | ||
} & CustomOptions; | ||
export declare type TSchema = TIntersect<any> | TUnion<any> | TTuple<any> | TObject<TProperties> | TDict<any> | TArray<any> | TEnum<any> | TLiteral<any> | TString | TNumber | TInteger | TBoolean | TNull | TUnknown | TAny | TConstructor<any[], any> | TFunction<any[], any> | TPromise<any> | TUndefined | TVoid; | ||
export declare type TSchema = TIntersect<any> | TUnion<any> | TTuple<any> | TObject<any> | TDict<any> | TArray<any> | TEnum<any> | TLiteral<any> | TString | TNumber | TInteger | TBoolean | TNull | TUnknown | TAny | TConstructor<any[], any> | TFunction<any[], any> | TPromise<any> | TUndefined | TVoid; | ||
export declare type TRequired<T extends TProperties> = { | ||
[K in keyof T]: T[K] extends TReadonlyOptional<infer U> ? TReadonly<U> : T[K] extends TReadonly<infer U> ? TReadonly<U> : T[K] extends TOptional<infer U> ? U : T[K]; | ||
}; | ||
export declare type TPartial<T extends TProperties> = { | ||
[K in keyof T]: T[K] extends TReadonlyOptional<infer U> ? TReadonlyOptional<U> : T[K] extends TReadonly<infer U> ? TReadonlyOptional<U> : T[K] extends TOptional<infer U> ? TOptional<U> : TOptional<T[K]>; | ||
}; | ||
export declare type UnionToIntersect<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; | ||
export declare type ReadonlyOptionalPropertyKeys<T> = { | ||
export declare type PropertyKeys<T extends TProperties> = keyof T; | ||
export declare type ReadonlyOptionalPropertyKeys<T extends TProperties> = { | ||
[K in keyof T]: T[K] extends TReadonlyOptional<infer U> ? K : never; | ||
}[keyof T]; | ||
export declare type ReadonlyPropertyKeys<T> = { | ||
export declare type ReadonlyPropertyKeys<T extends TProperties> = { | ||
[K in keyof T]: T[K] extends TReadonly<infer U> ? K : never; | ||
}[keyof T]; | ||
export declare type OptionalPropertyKeys<T> = { | ||
export declare type OptionalPropertyKeys<T extends TProperties> = { | ||
[K in keyof T]: T[K] extends TOptional<infer U> ? K : never; | ||
}[keyof T]; | ||
export declare type PropertyKeys<T> = keyof Omit<T, ReadonlyOptionalPropertyKeys<T> | ReadonlyPropertyKeys<T> | OptionalPropertyKeys<T>>; | ||
export declare type StaticProperties<T> = { | ||
export declare type RequiredPropertyKeys<T extends TProperties> = keyof Omit<T, ReadonlyOptionalPropertyKeys<T> | ReadonlyPropertyKeys<T> | OptionalPropertyKeys<T>>; | ||
export declare type StaticProperties<T extends TProperties> = { | ||
readonly [K in ReadonlyOptionalPropertyKeys<T>]?: Static<T[K]>; | ||
@@ -184,3 +191,3 @@ } & { | ||
} & { | ||
[K in PropertyKeys<T>]: Static<T[K]>; | ||
[K in RequiredPropertyKeys<T>]: Static<T[K]>; | ||
}; | ||
@@ -210,39 +217,39 @@ export declare type StaticIntersect<T extends readonly TSchema[]> = UnionToIntersect<StaticUnion<T>>; | ||
export declare class TypeBuilder { | ||
/** Modifies a schema object property to be `readonly` and `optional`. */ | ||
/** `STANDARD` Modifies a schema object property to be `readonly` and `optional`. */ | ||
ReadonlyOptional<T extends TSchema>(item: T): TReadonlyOptional<T>; | ||
/** Modifies a schema object property to be `readonly`. */ | ||
/** `STANDARD` Modifies a schema object property to be `readonly`. */ | ||
Readonly<T extends TSchema>(item: T): TReadonly<T>; | ||
/** Modifies a schema object property to be `optional`. */ | ||
/** `STANDARD` Modifies a schema object property to be `optional`. */ | ||
Optional<T extends TSchema>(item: T): TOptional<T>; | ||
/** Creates an Intersect schema. */ | ||
/** `STANDARD` Creates an Intersect schema. */ | ||
Intersect<T extends TSchema[]>(items: [...T], options?: CustomOptions): TIntersect<T>; | ||
/** Creates a Union schema. */ | ||
/** `STANDARD` Creates a Union schema. */ | ||
Union<T extends TSchema[]>(items: [...T], options?: CustomOptions): TUnion<T>; | ||
/** Creates a Tuple schema. */ | ||
/** `STANDARD` Creates a Tuple schema. */ | ||
Tuple<T extends TSchema[]>(items: [...T], options?: CustomOptions): TTuple<T>; | ||
/** Creates a `object` schema with the given properties. */ | ||
/** `STANDARD` Creates a `object` schema with the given properties. */ | ||
Object<T extends TProperties>(properties: T, options?: CustomOptions): TObject<T>; | ||
/** Creates a `{ [key: string]: T }` schema. */ | ||
/** `STANDARD` Creates a `{ [key: string]: T }` schema. */ | ||
Dict<T extends TSchema>(item: T, options?: DictOptions): TDict<T>; | ||
/** Creates an `Array<T>` schema. */ | ||
/** `STANDARD` Creates an `Array<T>` schema. */ | ||
Array<T extends TSchema>(items: T, options?: ArrayOptions): TArray<T>; | ||
/** Creates an `Enum<T>` schema from a TypeScript `enum` definition. */ | ||
/** `STANDARD` Creates an `Enum<T>` schema from a TypeScript `enum` definition. */ | ||
Enum<T extends TEnumType>(item: T, options?: CustomOptions): TEnum<T[keyof T]>; | ||
/** Creates a literal schema. Supports `string | number | boolean` values. */ | ||
/** `STANDARD` Creates a literal schema. Supports `string | number | boolean` values. */ | ||
Literal<T extends TValue>(value: T, options?: CustomOptions): TLiteral<T>; | ||
/** Creates a `string` schema. */ | ||
/** `STANDARD` Creates a `string` schema. */ | ||
String<TCustomFormatOption extends string>(options?: StringOptions<StringFormatOption | TCustomFormatOption>): TString; | ||
/** Creates a `string` schema from a regular expression. */ | ||
/** `STANDARD` Creates a `string` schema from a regular expression. */ | ||
RegEx(regex: RegExp, options?: CustomOptions): TString; | ||
/** Creates a `number` schema. */ | ||
/** `STANDARD` Creates a `number` schema. */ | ||
Number(options?: NumberOptions): TNumber; | ||
/** Creates a `integer` schema. */ | ||
/** `STANDARD` Creates a `integer` schema. */ | ||
Integer(options?: NumberOptions): TInteger; | ||
/** Creates a `boolean` type. */ | ||
/** `STANDARD` Creates a `boolean` schema. */ | ||
Boolean(options?: CustomOptions): TBoolean; | ||
/** Creates a `null` type. */ | ||
/** `STANDARD` Creates a `null` schema. */ | ||
Null(options?: CustomOptions): TNull; | ||
/** Creates an `unknown` type. */ | ||
/** `STANDARD` Creates an `unknown` schema. */ | ||
Unknown(options?: CustomOptions): TUnknown; | ||
/** Creates an `any` type. */ | ||
/** `STANDARD` Creates an `any` schema. */ | ||
Any(options?: CustomOptions): TAny; | ||
@@ -261,3 +268,11 @@ /** `EXTENDED` Creates a `constructor` schema. */ | ||
Strict<T extends TSchema>(schema: T): T; | ||
/** `UTILITY` Make all properties in schema object required. */ | ||
Required<T extends TObject<TProperties>>(schema: T): TObject<TRequired<T['properties']>>; | ||
/** `UTILITY` Make all properties in schema object optional. */ | ||
Partial<T extends TObject<TProperties>>(schema: T): TObject<TPartial<T['properties']>>; | ||
/** `UTILITY` Picks property keys from the given object schema. */ | ||
Pick<T extends TObject<TProperties>, K extends PropertyKeys<T['properties']>[]>(schema: T, keys: [...K]): TObject<Pick<T['properties'], K[number]>>; | ||
/** `UTILITY` Omits property keys from the given object schema. */ | ||
Omit<T extends TObject<TProperties>, K extends PropertyKeys<T['properties']>[]>(schema: T, keys: [...K]): TObject<Omit<T['properties'], K[number]>>; | ||
} | ||
export declare const Type: TypeBuilder; |
124
typebox.js
@@ -8,3 +8,3 @@ "use strict"; | ||
Copyright (c) 2020 Haydn Paterson (sinclair) <haydn.developer@gmail.com> | ||
Copyright (c) 2021 Haydn Paterson (sinclair) <haydn.developer@gmail.com> | ||
@@ -76,26 +76,43 @@ Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// ------------------------------------------------------------------------ | ||
// Clone | ||
// ------------------------------------------------------------------------ | ||
function clone(object) { | ||
if (typeof object === 'object' && !Array.isArray(object)) { | ||
return Object.keys(object).reduce((acc, key) => { | ||
acc[key] = clone(object[key]); | ||
return acc; | ||
}, {}); | ||
} | ||
else if (typeof object === 'object' && Array.isArray(object)) { | ||
return object.map((item) => clone(item)); | ||
} | ||
else { | ||
return object; | ||
} | ||
} | ||
// ------------------------------------------------------------------------ | ||
// TypeBuilder | ||
// ------------------------------------------------------------------------ | ||
class TypeBuilder { | ||
/** Modifies a schema object property to be `readonly` and `optional`. */ | ||
/** `STANDARD` Modifies a schema object property to be `readonly` and `optional`. */ | ||
ReadonlyOptional(item) { | ||
return { ...item, modifier: exports.ReadonlyOptionalModifier }; | ||
} | ||
/** Modifies a schema object property to be `readonly`. */ | ||
/** `STANDARD` Modifies a schema object property to be `readonly`. */ | ||
Readonly(item) { | ||
return { ...item, modifier: exports.ReadonlyModifier }; | ||
} | ||
/** Modifies a schema object property to be `optional`. */ | ||
/** `STANDARD` Modifies a schema object property to be `optional`. */ | ||
Optional(item) { | ||
return { ...item, modifier: exports.OptionalModifier }; | ||
} | ||
/** Creates an Intersect schema. */ | ||
/** `STANDARD` Creates an Intersect schema. */ | ||
Intersect(items, options = {}) { | ||
return { ...options, kind: exports.IntersectKind, allOf: items }; | ||
} | ||
/** Creates a Union schema. */ | ||
/** `STANDARD` Creates a Union schema. */ | ||
Union(items, options = {}) { | ||
return { ...options, kind: exports.UnionKind, anyOf: items }; | ||
} | ||
/** Creates a Tuple schema. */ | ||
/** `STANDARD` Creates a Tuple schema. */ | ||
Tuple(items, options = {}) { | ||
@@ -107,3 +124,3 @@ const additionalItems = false; | ||
} | ||
/** Creates a `object` schema with the given properties. */ | ||
/** `STANDARD` Creates a `object` schema with the given properties. */ | ||
Object(properties, options = {}) { | ||
@@ -119,2 +136,3 @@ const property_names = Object.keys(properties); | ||
const required = (required_names.length > 0) ? required_names : undefined; | ||
const additionalProperties = false; | ||
return (required) ? | ||
@@ -124,3 +142,3 @@ { ...options, kind: exports.ObjectKind, type: 'object', properties, required } : | ||
} | ||
/** Creates a `{ [key: string]: T }` schema. */ | ||
/** `STANDARD` Creates a `{ [key: string]: T }` schema. */ | ||
Dict(item, options = {}) { | ||
@@ -130,7 +148,7 @@ const additionalProperties = item; | ||
} | ||
/** Creates an `Array<T>` schema. */ | ||
/** `STANDARD` Creates an `Array<T>` schema. */ | ||
Array(items, options = {}) { | ||
return { ...options, kind: exports.ArrayKind, type: 'array', items }; | ||
} | ||
/** Creates an `Enum<T>` schema from a TypeScript `enum` definition. */ | ||
/** `STANDARD` Creates an `Enum<T>` schema from a TypeScript `enum` definition. */ | ||
Enum(item, options = {}) { | ||
@@ -140,3 +158,3 @@ const values = Object.keys(item).filter(key => isNaN(key)).map(key => item[key]); | ||
} | ||
/** Creates a literal schema. Supports `string | number | boolean` values. */ | ||
/** `STANDARD` Creates a literal schema. Supports `string | number | boolean` values. */ | ||
Literal(value, options = {}) { | ||
@@ -149,31 +167,31 @@ const type = reflect(value); | ||
} | ||
/** Creates a `string` schema. */ | ||
/** `STANDARD` Creates a `string` schema. */ | ||
String(options = {}) { | ||
return { ...options, kind: exports.StringKind, type: 'string' }; | ||
} | ||
/** Creates a `string` schema from a regular expression. */ | ||
/** `STANDARD` Creates a `string` schema from a regular expression. */ | ||
RegEx(regex, options = {}) { | ||
return this.String({ ...options, pattern: regex.source }); | ||
} | ||
/** Creates a `number` schema. */ | ||
/** `STANDARD` Creates a `number` schema. */ | ||
Number(options = {}) { | ||
return { ...options, kind: exports.NumberKind, type: 'number' }; | ||
} | ||
/** Creates a `integer` schema. */ | ||
/** `STANDARD` Creates a `integer` schema. */ | ||
Integer(options = {}) { | ||
return { ...options, kind: exports.IntegerKind, type: 'integer' }; | ||
} | ||
/** Creates a `boolean` type. */ | ||
/** `STANDARD` Creates a `boolean` schema. */ | ||
Boolean(options = {}) { | ||
return { ...options, kind: exports.BooleanKind, type: 'boolean' }; | ||
} | ||
/** Creates a `null` type. */ | ||
/** `STANDARD` Creates a `null` schema. */ | ||
Null(options = {}) { | ||
return { ...options, kind: exports.NullKind, type: 'null' }; | ||
} | ||
/** Creates an `unknown` type. */ | ||
/** `STANDARD` Creates an `unknown` schema. */ | ||
Unknown(options = {}) { | ||
return { ...options, kind: exports.UnknownKind }; | ||
} | ||
/** Creates an `any` type. */ | ||
/** `STANDARD` Creates an `any` schema. */ | ||
Any(options = {}) { | ||
@@ -206,4 +224,70 @@ return { ...options, kind: exports.AnyKind }; | ||
} | ||
/** `UTILITY` Make all properties in schema object required. */ | ||
Required(schema) { | ||
const next = clone(schema); | ||
next.required = Object.keys(next.properties); | ||
for (const key of Object.keys(next.properties)) { | ||
const property = next.properties[key]; | ||
switch (property.modifier) { | ||
case exports.ReadonlyOptionalModifier: | ||
property.modifier = exports.ReadonlyModifier; | ||
break; | ||
case exports.ReadonlyModifier: | ||
property.modifier = exports.ReadonlyModifier; | ||
break; | ||
case exports.OptionalModifier: | ||
delete property.modifier; | ||
break; | ||
default: | ||
delete property.modifier; | ||
break; | ||
} | ||
} | ||
return next; | ||
} | ||
/** `UTILITY` Make all properties in schema object optional. */ | ||
Partial(schema) { | ||
const next = clone(schema); | ||
delete next.required; | ||
for (const key of Object.keys(next.properties)) { | ||
const property = next.properties[key]; | ||
switch (property.modifier) { | ||
case exports.ReadonlyOptionalModifier: | ||
property.modifier = exports.ReadonlyOptionalModifier; | ||
break; | ||
case exports.ReadonlyModifier: | ||
property.modifier = exports.ReadonlyOptionalModifier; | ||
break; | ||
case exports.OptionalModifier: | ||
property.modifier = exports.OptionalModifier; | ||
break; | ||
default: | ||
property.modifier = exports.OptionalModifier; | ||
break; | ||
} | ||
} | ||
return next; | ||
} | ||
/** `UTILITY` Picks property keys from the given object schema. */ | ||
Pick(schema, keys) { | ||
const next = clone(schema); | ||
next.required = next.required ? next.required.filter((key) => keys.includes(key)) : undefined; | ||
for (const key of Object.keys(next.properties)) { | ||
if (!keys.includes(key)) | ||
delete next.properties[key]; | ||
} | ||
return next; | ||
} | ||
/** `UTILITY` Omits property keys from the given object schema. */ | ||
Omit(schema, keys) { | ||
const next = clone(schema); | ||
next.required = next.required ? next.required.filter((key) => !keys.includes(key)) : undefined; | ||
for (const key of Object.keys(next.properties)) { | ||
if (keys.includes(key)) | ||
delete next.properties[key]; | ||
} | ||
return next; | ||
} | ||
} | ||
exports.TypeBuilder = TypeBuilder; | ||
exports.Type = new TypeBuilder(); |
72372
554
605
11