@sinclair/typebox 0.22.0 → 0.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/package.json +1 -1
  2. package/readme.md +209 -258
  3. package/typebox.d.ts +54 -41
  4. package/typebox.js +103 -66
package/typebox.d.ts CHANGED
@@ -28,6 +28,7 @@ export declare const BooleanKind: unique symbol;
28
28
  export declare const NullKind: unique symbol;
29
29
  export declare const UnknownKind: unique symbol;
30
30
  export declare const AnyKind: unique symbol;
31
+ export declare const RefKind: unique symbol;
31
32
  export interface CustomOptions {
32
33
  $id?: string;
33
34
  title?: string;
@@ -71,7 +72,7 @@ export declare type TNamespace<T extends TDefinitions> = {
71
72
  $defs: T;
72
73
  } & CustomOptions;
73
74
  export interface TSchema {
74
- 'typebox:output': unknown;
75
+ $static: unknown;
75
76
  }
76
77
  export declare type TEnumType = Record<string, string | number>;
77
78
  export declare type TKey = string | number | symbol;
@@ -85,7 +86,7 @@ export interface TProperties {
85
86
  [key: string]: TSchema;
86
87
  }
87
88
  export interface TRecord<K extends TRecordKey, T extends TSchema> extends TSchema, ObjectOptions {
88
- 'typebox:output': StaticRecord<K, T>;
89
+ $static: StaticRecord<K, T>;
89
90
  kind: typeof RecordKind;
90
91
  type: 'object';
91
92
  patternProperties: {
@@ -93,7 +94,7 @@ export interface TRecord<K extends TRecordKey, T extends TSchema> extends TSchem
93
94
  };
94
95
  }
95
96
  export interface TTuple<T extends TSchema[]> extends TSchema, CustomOptions {
96
- 'typebox:output': StaticTuple<T>;
97
+ $static: StaticTuple<T>;
97
98
  kind: typeof TupleKind;
98
99
  type: 'array';
99
100
  items?: T;
@@ -102,76 +103,81 @@ export interface TTuple<T extends TSchema[]> extends TSchema, CustomOptions {
102
103
  maxItems: number;
103
104
  }
104
105
  export interface TObject<T extends TProperties> extends TSchema, ObjectOptions {
105
- 'typebox:output': StaticObject<T>;
106
+ $static: StaticObject<T>;
106
107
  kind: typeof ObjectKind;
107
108
  type: 'object';
108
109
  properties: T;
109
110
  required?: string[];
110
111
  }
111
112
  export interface TUnion<T extends TSchema[]> extends TSchema, CustomOptions {
112
- 'typebox:output': StaticUnion<T>;
113
+ $static: StaticUnion<T>;
113
114
  kind: typeof UnionKind;
114
115
  anyOf: T;
115
116
  }
116
117
  export interface TIntersect<T extends TSchema[]> extends TSchema, IntersectOptions {
117
- 'typebox:output': StaticIntersect<T>;
118
+ $static: StaticIntersect<T>;
118
119
  kind: typeof IntersectKind;
119
120
  type: 'object';
120
121
  allOf: T;
121
122
  }
122
123
  export interface TKeyOf<T extends TKey[]> extends TSchema, CustomOptions {
123
- 'typebox:output': StaticKeyOf<T>;
124
+ $static: StaticKeyOf<T>;
124
125
  kind: typeof KeyOfKind;
125
126
  type: 'string';
126
127
  enum: T;
127
128
  }
128
129
  export interface TArray<T extends TSchema> extends TSchema, ArrayOptions {
129
- 'typebox:output': StaticArray<T>;
130
+ $static: StaticArray<T>;
130
131
  kind: typeof ArrayKind;
131
132
  type: 'array';
132
133
  items: T;
133
134
  }
134
135
  export interface TLiteral<T extends TValue> extends TSchema, CustomOptions {
135
- 'typebox:output': StaticLiteral<T>;
136
+ $static: StaticLiteral<T>;
136
137
  kind: typeof LiteralKind;
137
138
  const: T;
138
139
  }
139
140
  export interface TEnum<T extends TEnumKey[]> extends TSchema, CustomOptions {
140
- 'typebox:output': StaticEnum<T>;
141
+ $static: StaticEnum<T>;
141
142
  kind: typeof EnumKind;
142
143
  anyOf: T;
143
144
  }
145
+ export interface TRef<T extends TSchema> extends TSchema, CustomOptions {
146
+ $static: Static<T>;
147
+ kind: typeof RefKind;
148
+ $ref: string;
149
+ }
144
150
  export interface TString extends TSchema, StringOptions<string> {
145
- 'typebox:output': string;
151
+ $static: string;
146
152
  kind: typeof StringKind;
147
153
  type: 'string';
148
154
  }
149
155
  export interface TNumber extends TSchema, NumberOptions {
150
- 'typebox:output': number;
156
+ $static: number;
151
157
  kind: typeof NumberKind;
152
158
  type: 'number';
153
159
  }
154
160
  export interface TInteger extends TSchema, NumberOptions {
155
- 'typebox:output': number;
161
+ $static: number;
156
162
  kind: typeof IntegerKind;
157
163
  type: 'integer';
158
164
  }
159
165
  export interface TBoolean extends TSchema, CustomOptions {
160
- 'typebox:output': boolean;
166
+ $static: boolean;
161
167
  kind: typeof BooleanKind;
162
168
  type: 'boolean';
163
169
  }
164
170
  export interface TNull extends TSchema, CustomOptions {
165
- 'typebox:output': null;
171
+ $static: null;
166
172
  kind: typeof NullKind;
167
173
  type: 'null';
168
174
  }
169
175
  export interface TUnknown extends TSchema, CustomOptions {
170
- 'typebox:output': unknown;
176
+ $static: unknown;
171
177
  kind: typeof UnknownKind;
172
178
  }
173
179
  export interface TAny extends TSchema, CustomOptions {
174
- 'typebox:output': any;
180
+ $static: any;
175
181
  kind: typeof AnyKind;
176
182
  }
177
183
  export declare const ConstructorKind: unique symbol;
@@ -180,35 +186,38 @@ export declare const PromiseKind: unique symbol;
180
186
  export declare const UndefinedKind: unique symbol;
181
187
  export declare const VoidKind: unique symbol;
182
188
  export interface TConstructor<T extends TSchema[], U extends TSchema> extends TSchema, CustomOptions {
183
- 'typebox:output': StaticConstructor<T, U>;
189
+ $static: StaticConstructor<T, U>;
184
190
  kind: typeof ConstructorKind;
185
191
  type: 'constructor';
186
192
  arguments: TSchema[];
187
193
  returns: TSchema;
188
194
  }
189
195
  export interface TFunction<T extends TSchema[], U extends TSchema> extends TSchema, CustomOptions {
190
- 'typebox:output': StaticFunction<T, U>;
196
+ $static: StaticFunction<T, U>;
191
197
  kind: typeof FunctionKind;
192
198
  type: 'function';
193
199
  arguments: TSchema[];
194
200
  returns: TSchema;
195
201
  }
196
202
  export interface TPromise<T extends TSchema> extends TSchema, CustomOptions {
197
- 'typebox:output': StaticPromise<T>;
203
+ $static: StaticPromise<T>;
198
204
  kind: typeof PromiseKind;
199
205
  type: 'promise';
200
206
  item: TSchema;
201
207
  }
202
208
  export interface TUndefined extends TSchema, CustomOptions {
203
- 'typebox:output': undefined;
209
+ $static: undefined;
204
210
  kind: typeof UndefinedKind;
205
211
  type: 'undefined';
206
212
  }
207
213
  export interface TVoid extends TSchema, CustomOptions {
208
- 'typebox:output': void;
214
+ $static: void;
209
215
  kind: typeof VoidKind;
210
216
  type: 'void';
211
217
  }
218
+ export declare type Selectable = TObject<TProperties> | TRef<TObject<TProperties>>;
219
+ export declare type SelectablePropertyKeys<T extends Selectable> = T extends TObject<infer U> ? keyof U : T extends TRef<TObject<infer U>> ? keyof U : never;
220
+ export declare type SelectableProperties<T extends Selectable> = T extends TObject<infer U> ? U : T extends TRef<TObject<infer U>> ? U : never;
212
221
  export declare type UnionToIntersect<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
213
222
  export declare type StaticReadonlyOptionalPropertyKeys<T extends TProperties> = {
214
223
  [K in keyof T]: T[K] extends TReadonlyOptional<TSchema> ? K : never;
@@ -221,7 +230,7 @@ export declare type StaticOptionalPropertyKeys<T extends TProperties> = {
221
230
  }[keyof T];
222
231
  export declare type StaticRequiredPropertyKeys<T extends TProperties> = keyof Omit<T, StaticReadonlyOptionalPropertyKeys<T> | StaticReadonlyPropertyKeys<T> | StaticOptionalPropertyKeys<T>>;
223
232
  export declare type StaticIntersectEvaluate<T extends readonly TSchema[]> = {
224
- [K in keyof T]: Static<T[K]>;
233
+ [K in keyof T]: T[K] extends TSchema ? Static<T[K]> : never;
225
234
  };
226
235
  export declare type StaticIntersectReduce<I extends unknown, T extends readonly any[]> = T extends [infer A, ...infer B] ? StaticIntersectReduce<I & A, B> : I;
227
236
  export declare type StaticRequired<T extends TProperties> = {
@@ -239,15 +248,15 @@ export declare type StaticProperties<T extends TProperties> = {
239
248
  } & {
240
249
  [K in StaticRequiredPropertyKeys<T>]: Static<T[K]>;
241
250
  };
242
- export declare type StaticRecord<K extends TRecordKey, T extends TSchema> = K extends TString ? Record<string, Static<T>> : K extends TNumber ? Record<number, Static<T>> : K extends TKeyOf<TKey[]> ? Record<K['typebox:output'], Static<T>> : K extends TUnion<TSchema[]> ? Record<K['typebox:output'], Static<T>> : never;
251
+ export declare type StaticRecord<K extends TRecordKey, T extends TSchema> = K extends TString ? Record<string, Static<T>> : K extends TNumber ? Record<number, Static<T>> : K extends TKeyOf<TKey[]> ? Record<K['$static'], Static<T>> : K extends TUnion<TSchema[]> ? Record<K['$static'], Static<T>> : never;
243
252
  export declare type StaticEnum<T> = T extends TEnumKey<infer U>[] ? U : never;
244
253
  export declare type StaticKeyOf<T extends TKey[]> = T extends Array<infer K> ? K : never;
245
254
  export declare type StaticIntersect<T extends readonly TSchema[]> = StaticIntersectReduce<unknown, StaticIntersectEvaluate<T>>;
246
255
  export declare type StaticUnion<T extends readonly TSchema[]> = {
247
- [K in keyof T]: Static<T[K]>;
256
+ [K in keyof T]: T[K] extends TSchema ? Static<T[K]> : never;
248
257
  }[number];
249
258
  export declare type StaticTuple<T extends readonly TSchema[]> = {
250
- [K in keyof T]: Static<T[K]>;
259
+ [K in keyof T]: T[K] extends TSchema ? Static<T[K]> : never;
251
260
  };
252
261
  export declare type StaticObject<T extends TProperties> = StaticProperties<T> extends infer I ? {
253
262
  [K in keyof I]: I[K];
@@ -255,13 +264,14 @@ export declare type StaticObject<T extends TProperties> = StaticProperties<T> ex
255
264
  export declare type StaticArray<T extends TSchema> = Array<Static<T>>;
256
265
  export declare type StaticLiteral<T extends TValue> = T;
257
266
  export declare type StaticParameters<T extends readonly TSchema[]> = {
258
- [K in keyof T]: Static<T[K]>;
267
+ [K in keyof T]: T[K] extends TSchema ? Static<T[K]> : never;
259
268
  };
260
269
  export declare type StaticConstructor<T extends readonly TSchema[], U extends TSchema> = new (...args: [...StaticParameters<T>]) => Static<U>;
261
270
  export declare type StaticFunction<T extends readonly TSchema[], U extends TSchema> = (...args: [...StaticParameters<T>]) => Static<U>;
262
271
  export declare type StaticPromise<T extends TSchema> = Promise<Static<T>>;
263
- export declare type Static<T> = T extends TSchema ? T['typebox:output'] : never;
272
+ export declare type Static<T extends TSchema> = T['$static'];
264
273
  export declare class TypeBuilder {
274
+ protected readonly schemas: Map<string, TSchema>;
265
275
  /** `Standard` Modifies an object property to be both readonly and optional */
266
276
  ReadonlyOptional<T extends TSchema>(item: T): TReadonlyOptional<T>;
267
277
  /** `Standard` Modifies an object property to be readonly */
@@ -298,18 +308,18 @@ export declare class TypeBuilder {
298
308
  Unknown(options?: CustomOptions): TUnknown;
299
309
  /** `Standard` Creates an any type */
300
310
  Any(options?: CustomOptions): TAny;
301
- /** `Standard` Creates a keyof type from the given object */
302
- KeyOf<T extends TObject<TProperties>>(schema: T, options?: CustomOptions): TKeyOf<(keyof T['properties'])[]>;
303
311
  /** `Standard` Creates a record type */
304
312
  Record<K extends TRecordKey, T extends TSchema>(key: K, value: T, options?: ObjectOptions): TRecord<K, T>;
313
+ /** `Standard` Creates a keyof type from the given object */
314
+ KeyOf<T extends TObject<TProperties> | TRef<TObject<TProperties>>>(object: T, options?: CustomOptions): TKeyOf<SelectablePropertyKeys<T>[]>;
305
315
  /** `Standard` Makes all properties in the given object type required */
306
- Required<T extends TObject<any>>(schema: T, options?: ObjectOptions): TObject<StaticRequired<T['properties']>>;
316
+ Required<T extends TObject<TProperties> | TRef<TObject<TProperties>>>(object: T, options?: ObjectOptions): TObject<StaticRequired<T['properties']>>;
307
317
  /** `Standard` Makes all properties in the given object type optional */
308
- Partial<T extends TObject<any>>(schema: T, options?: ObjectOptions): TObject<StaticPartial<T['properties']>>;
318
+ Partial<T extends TObject<TProperties> | TRef<TObject<TProperties>>>(object: T, options?: ObjectOptions): TObject<StaticPartial<T['properties']>>;
309
319
  /** `Standard` Picks property keys from the given object type */
310
- Pick<T extends TObject<TProperties>, K extends (keyof T['properties'])[]>(schema: T, keys: [...K], options?: ObjectOptions): TObject<Pick<T['properties'], K[number]>>;
320
+ Pick<T extends TObject<TProperties> | TRef<TObject<TProperties>>, K extends SelectablePropertyKeys<T>[]>(object: T, keys: [...K], options?: ObjectOptions): TObject<Pick<SelectableProperties<T>, K[number]>>;
311
321
  /** `Standard` Omits property keys from the given object type */
312
- Omit<T extends TObject<any>, K extends (keyof T['properties'])[]>(schema: T, keys: [...K], options?: ObjectOptions): TObject<Omit<T['properties'], K[number]>>;
322
+ Omit<T extends TObject<TProperties> | TRef<TObject<TProperties>>, K extends SelectablePropertyKeys<T>[]>(object: T, keys: [...K], options?: ObjectOptions): TObject<Omit<SelectableProperties<T>, K[number]>>;
313
323
  /** `Standard` Omits the `kind` and `modifier` properties from the underlying schema */
314
324
  Strict<T extends TSchema>(schema: T, options?: CustomOptions): T;
315
325
  /** `Extended` Creates a constructor type */
@@ -322,14 +332,17 @@ export declare class TypeBuilder {
322
332
  Undefined(options?: CustomOptions): TUndefined;
323
333
  /** `Extended` Creates a void type */
324
334
  Void(options?: CustomOptions): TVoid;
335
+ /** `Standard` Creates a namespace for a set of related types */
336
+ Namespace<T extends TDefinitions>($defs: T, options?: CustomOptions): TNamespace<T>;
337
+ /** `Standard` References a type within a namespace. The referenced namespace must specify an `$id` */
338
+ Ref<T extends TNamespace<TDefinitions>, K extends keyof T['$defs']>(namespace: T, key: K): TRef<T['$defs'][K]>;
339
+ /** `Standard` References type. The referenced type must specify an `$id` */
340
+ Ref<T extends TSchema>(schema: T): TRef<T>;
325
341
  /** `Experimental` Creates a recursive type */
326
342
  Rec<T extends TSchema>(callback: (self: TAny) => T, options?: CustomOptions): T;
327
- /** `Experimental` Creates a recursive type. Pending https://github.com/ajv-validator/ajv/issues/1709 */
328
- /** `Experimental` Creates a namespace for a set of related types */
329
- Namespace<T extends TDefinitions>($defs: T, options?: CustomOptions): TNamespace<T>;
330
- /** `Experimental` References a type within a namespace. The referenced namespace must specify an `$id` */
331
- Ref<T extends TNamespace<TDefinitions>, K extends keyof T['$defs']>(box: T, key: K): T['$defs'][K];
332
- /** `Experimental` References type. The referenced type must specify an `$id` */
333
- Ref<T extends TSchema>(schema: T): T;
343
+ /** Conditionally stores and schema if it contains an $id and returns */
344
+ protected Store<T extends TSchema | TNamespace<TDefinitions>, S = Omit<T, '$static'>>(schema: S): T;
345
+ /** Conditionally dereferences a schema if RefKind. Otherwise return argument */
346
+ protected Deref<T extends TSchema>(schema: T): any;
334
347
  }
335
348
  export declare const Type: TypeBuilder;
package/typebox.js CHANGED
@@ -27,7 +27,7 @@ THE SOFTWARE.
27
27
 
28
28
  ---------------------------------------------------------------------------*/
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.Type = exports.TypeBuilder = exports.VoidKind = exports.UndefinedKind = exports.PromiseKind = exports.FunctionKind = exports.ConstructorKind = exports.AnyKind = exports.UnknownKind = exports.NullKind = exports.BooleanKind = exports.IntegerKind = exports.NumberKind = exports.StringKind = exports.LiteralKind = exports.EnumKind = exports.ArrayKind = exports.RecordKind = exports.ObjectKind = exports.TupleKind = exports.UnionKind = exports.IntersectKind = exports.KeyOfKind = exports.BoxKind = exports.ReadonlyModifier = exports.OptionalModifier = exports.ReadonlyOptionalModifier = void 0;
30
+ exports.Type = exports.TypeBuilder = exports.VoidKind = exports.UndefinedKind = exports.PromiseKind = exports.FunctionKind = exports.ConstructorKind = exports.RefKind = exports.AnyKind = exports.UnknownKind = exports.NullKind = exports.BooleanKind = exports.IntegerKind = exports.NumberKind = exports.StringKind = exports.LiteralKind = exports.EnumKind = exports.ArrayKind = exports.RecordKind = exports.ObjectKind = exports.TupleKind = exports.UnionKind = exports.IntersectKind = exports.KeyOfKind = exports.BoxKind = exports.ReadonlyModifier = exports.OptionalModifier = exports.ReadonlyOptionalModifier = void 0;
31
31
  // --------------------------------------------------------------------------
32
32
  // Modifiers
33
33
  // --------------------------------------------------------------------------
@@ -54,6 +54,7 @@ exports.BooleanKind = Symbol('BooleanKind');
54
54
  exports.NullKind = Symbol('NullKind');
55
55
  exports.UnknownKind = Symbol('UnknownKind');
56
56
  exports.AnyKind = Symbol('AnyKind');
57
+ exports.RefKind = Symbol('RefKind');
57
58
  // --------------------------------------------------------------------------
58
59
  // Extended Schema Types
59
60
  // --------------------------------------------------------------------------
@@ -82,6 +83,7 @@ function clone(object) {
82
83
  // TypeBuilder
83
84
  // --------------------------------------------------------------------------
84
85
  class TypeBuilder {
86
+ schemas = new Map();
85
87
  /** `Standard` Modifies an object property to be both readonly and optional */
86
88
  ReadonlyOptional(item) {
87
89
  return { ...item, modifier: exports.ReadonlyOptionalModifier };
@@ -99,9 +101,10 @@ class TypeBuilder {
99
101
  const additionalItems = false;
100
102
  const minItems = items.length;
101
103
  const maxItems = items.length;
102
- return ((items.length > 0)
104
+ const schema = ((items.length > 0)
103
105
  ? { ...options, kind: exports.TupleKind, type: 'array', items, additionalItems, minItems, maxItems }
104
106
  : { ...options, kind: exports.TupleKind, type: 'array', minItems, maxItems });
107
+ return this.Store(schema);
105
108
  }
106
109
  /** `Standard` Creates an object type with the given properties */
107
110
  Object(properties, options = {}) {
@@ -114,35 +117,35 @@ class TypeBuilder {
114
117
  });
115
118
  const required_names = property_names.filter(name => !optional.includes(name));
116
119
  const required = (required_names.length > 0) ? required_names : undefined;
117
- return ((required)
120
+ return this.Store(((required)
118
121
  ? { ...options, kind: exports.ObjectKind, type: 'object', properties, required }
119
- : { ...options, kind: exports.ObjectKind, type: 'object', properties });
122
+ : { ...options, kind: exports.ObjectKind, type: 'object', properties }));
120
123
  }
121
124
  /** `Standard` Creates an intersect type. */
122
125
  Intersect(items, options = {}) {
123
- return { ...options, kind: exports.IntersectKind, type: 'object', allOf: items };
126
+ return this.Store({ ...options, kind: exports.IntersectKind, type: 'object', allOf: items });
124
127
  }
125
128
  /** `Standard` Creates a union type */
126
129
  Union(items, options = {}) {
127
- return { ...options, kind: exports.UnionKind, anyOf: items };
130
+ return this.Store({ ...options, kind: exports.UnionKind, anyOf: items });
128
131
  }
129
132
  /** `Standard` Creates an array type */
130
133
  Array(items, options = {}) {
131
- return { ...options, kind: exports.ArrayKind, type: 'array', items };
134
+ return this.Store({ ...options, kind: exports.ArrayKind, type: 'array', items });
132
135
  }
133
136
  /** `Standard` Creates an enum type from a TypeScript enum */
134
137
  Enum(item, options = {}) {
135
138
  const values = Object.keys(item).filter(key => isNaN(key)).map(key => item[key]);
136
139
  const anyOf = values.map(value => typeof value === 'string' ? { type: 'string', const: value } : { type: 'number', const: value });
137
- return { ...options, kind: exports.EnumKind, anyOf };
140
+ return this.Store({ ...options, kind: exports.EnumKind, anyOf });
138
141
  }
139
142
  /** `Standard` Creates a literal type. Supports string, number and boolean values only */
140
143
  Literal(value, options = {}) {
141
- return { ...options, kind: exports.LiteralKind, const: value, type: typeof value };
144
+ return this.Store({ ...options, kind: exports.LiteralKind, const: value, type: typeof value });
142
145
  }
143
146
  /** `Standard` Creates a string type */
144
147
  String(options = {}) {
145
- return { ...options, kind: exports.StringKind, type: 'string' };
148
+ return this.Store({ ...options, kind: exports.StringKind, type: 'string' });
146
149
  }
147
150
  /** `Standard` Creates a string type from a regular expression */
148
151
  RegEx(regex, options = {}) {
@@ -150,32 +153,27 @@ class TypeBuilder {
150
153
  }
151
154
  /** `Standard` Creates a number type */
152
155
  Number(options = {}) {
153
- return { ...options, kind: exports.NumberKind, type: 'number' };
156
+ return this.Store({ ...options, kind: exports.NumberKind, type: 'number' });
154
157
  }
155
158
  /** `Standard` Creates an integer type */
156
159
  Integer(options = {}) {
157
- return { ...options, kind: exports.IntegerKind, type: 'integer' };
160
+ return this.Store({ ...options, kind: exports.IntegerKind, type: 'integer' });
158
161
  }
159
162
  /** `Standard` Creates a boolean type */
160
163
  Boolean(options = {}) {
161
- return { ...options, kind: exports.BooleanKind, type: 'boolean' };
164
+ return this.Store({ ...options, kind: exports.BooleanKind, type: 'boolean' });
162
165
  }
163
166
  /** `Standard` Creates a null type */
164
167
  Null(options = {}) {
165
- return { ...options, kind: exports.NullKind, type: 'null' };
168
+ return this.Store({ ...options, kind: exports.NullKind, type: 'null' });
166
169
  }
167
170
  /** `Standard` Creates an unknown type */
168
171
  Unknown(options = {}) {
169
- return { ...options, kind: exports.UnknownKind };
172
+ return this.Store({ ...options, kind: exports.UnknownKind });
170
173
  }
171
174
  /** `Standard` Creates an any type */
172
175
  Any(options = {}) {
173
- return { ...options, kind: exports.AnyKind };
174
- }
175
- /** `Standard` Creates a keyof type from the given object */
176
- KeyOf(schema, options = {}) {
177
- const keys = Object.keys(schema.properties);
178
- return { ...options, kind: exports.KeyOfKind, type: 'string', enum: keys };
176
+ return this.Store({ ...options, kind: exports.AnyKind });
179
177
  }
180
178
  /** `Standard` Creates a record type */
181
179
  Record(key, value, options = {}) {
@@ -188,14 +186,21 @@ class TypeBuilder {
188
186
  default: throw Error('Invalid Record Key');
189
187
  }
190
188
  })();
191
- return { ...options, kind: exports.RecordKind, type: 'object', patternProperties: { [pattern]: value } };
189
+ return this.Store({ ...options, kind: exports.RecordKind, type: 'object', patternProperties: { [pattern]: value } });
190
+ }
191
+ /** `Standard` Creates a keyof type from the given object */
192
+ KeyOf(object, options = {}) {
193
+ const source = this.Deref(object);
194
+ const keys = Object.keys(source.properties);
195
+ return this.Store({ ...options, kind: exports.KeyOfKind, type: 'string', enum: keys });
192
196
  }
193
197
  /** `Standard` Makes all properties in the given object type required */
194
- Required(schema, options = {}) {
195
- const next = { ...clone(schema), ...options };
196
- next.required = Object.keys(next.properties);
197
- for (const key of Object.keys(next.properties)) {
198
- const property = next.properties[key];
198
+ Required(object, options = {}) {
199
+ const source = this.Deref(object);
200
+ const schema = { ...clone(source), ...options };
201
+ schema.required = Object.keys(schema.properties);
202
+ for (const key of Object.keys(schema.properties)) {
203
+ const property = schema.properties[key];
199
204
  switch (property.modifier) {
200
205
  case exports.ReadonlyOptionalModifier:
201
206
  property.modifier = exports.ReadonlyModifier;
@@ -211,14 +216,15 @@ class TypeBuilder {
211
216
  break;
212
217
  }
213
218
  }
214
- return next;
219
+ return this.Store(schema);
215
220
  }
216
221
  /** `Standard` Makes all properties in the given object type optional */
217
- Partial(schema, options = {}) {
218
- const next = { ...clone(schema), ...options };
219
- delete next.required;
220
- for (const key of Object.keys(next.properties)) {
221
- const property = next.properties[key];
222
+ Partial(object, options = {}) {
223
+ const source = this.Deref(object);
224
+ const schema = { ...clone(source), ...options };
225
+ delete schema.required;
226
+ for (const key of Object.keys(schema.properties)) {
227
+ const property = schema.properties[key];
222
228
  switch (property.modifier) {
223
229
  case exports.ReadonlyOptionalModifier:
224
230
  property.modifier = exports.ReadonlyOptionalModifier;
@@ -234,27 +240,29 @@ class TypeBuilder {
234
240
  break;
235
241
  }
236
242
  }
237
- return next;
243
+ return this.Store(schema);
238
244
  }
239
245
  /** `Standard` Picks property keys from the given object type */
240
- Pick(schema, keys, options = {}) {
241
- const next = { ...clone(schema), ...options };
242
- next.required = next.required ? next.required.filter((key) => keys.includes(key)) : undefined;
243
- for (const key of Object.keys(next.properties)) {
246
+ Pick(object, keys, options = {}) {
247
+ const source = this.Deref(object);
248
+ const schema = { ...clone(source), ...options };
249
+ schema.required = schema.required ? schema.required.filter((key) => keys.includes(key)) : undefined;
250
+ for (const key of Object.keys(schema.properties)) {
244
251
  if (!keys.includes(key))
245
- delete next.properties[key];
252
+ delete schema.properties[key];
246
253
  }
247
- return next;
254
+ return this.Store(schema);
248
255
  }
249
256
  /** `Standard` Omits property keys from the given object type */
250
- Omit(schema, keys, options = {}) {
251
- const next = { ...clone(schema), ...options };
252
- next.required = next.required ? next.required.filter((key) => !keys.includes(key)) : undefined;
253
- for (const key of Object.keys(next.properties)) {
257
+ Omit(object, keys, options = {}) {
258
+ const source = this.Deref(object);
259
+ const schema = { ...clone(source), ...options };
260
+ schema.required = schema.required ? schema.required.filter((key) => !keys.includes(key)) : undefined;
261
+ for (const key of Object.keys(schema.properties)) {
254
262
  if (keys.includes(key))
255
- delete next.properties[key];
263
+ delete schema.properties[key];
256
264
  }
257
- return next;
265
+ return this.Store(schema);
258
266
  }
259
267
  /** `Standard` Omits the `kind` and `modifier` properties from the underlying schema */
260
268
  Strict(schema, options = {}) {
@@ -262,43 +270,72 @@ class TypeBuilder {
262
270
  }
263
271
  /** `Extended` Creates a constructor type */
264
272
  Constructor(args, returns, options = {}) {
265
- return { ...options, kind: exports.ConstructorKind, type: 'constructor', arguments: args, returns };
273
+ return this.Store({ ...options, kind: exports.ConstructorKind, type: 'constructor', arguments: args, returns });
266
274
  }
267
275
  /** `Extended` Creates a function type */
268
276
  Function(args, returns, options = {}) {
269
- return { ...options, kind: exports.FunctionKind, type: 'function', arguments: args, returns };
277
+ return this.Store({ ...options, kind: exports.FunctionKind, type: 'function', arguments: args, returns });
270
278
  }
271
279
  /** `Extended` Creates a promise type */
272
280
  Promise(item, options = {}) {
273
- return { ...options, type: 'promise', kind: exports.PromiseKind, item };
281
+ return this.Store({ ...options, type: 'promise', kind: exports.PromiseKind, item });
274
282
  }
275
283
  /** `Extended` Creates a undefined type */
276
284
  Undefined(options = {}) {
277
- return { ...options, type: 'undefined', kind: exports.UndefinedKind };
285
+ return this.Store({ ...options, type: 'undefined', kind: exports.UndefinedKind });
278
286
  }
279
287
  /** `Extended` Creates a void type */
280
288
  Void(options = {}) {
281
- return { ...options, type: 'void', kind: exports.VoidKind };
289
+ return this.Store({ ...options, type: 'void', kind: exports.VoidKind });
290
+ }
291
+ /** `Standard` Creates a namespace for a set of related types */
292
+ Namespace($defs, options = {}) {
293
+ return this.Store({ ...options, kind: exports.BoxKind, $defs });
294
+ }
295
+ Ref(...args) {
296
+ if (args.length === 2) {
297
+ const namespace = args[0];
298
+ const targetKey = args[1];
299
+ if (namespace.$id === undefined)
300
+ throw new Error(`Referenced namespace has no $id`);
301
+ if (!this.schemas.has(namespace.$id))
302
+ throw new Error(`Unable to locate namespace with $id '${namespace.$id}'`);
303
+ return this.Store({ kind: exports.RefKind, $ref: `${namespace.$id}#/$defs/${targetKey}` });
304
+ }
305
+ else if (args.length === 1) {
306
+ const target = args[0];
307
+ if (target.$id === undefined)
308
+ throw new Error(`Referenced schema has no $id`);
309
+ if (!this.schemas.has(target.$id))
310
+ throw new Error(`Unable to locate schema with $id '${target.$id}'`);
311
+ return this.Store({ kind: exports.RefKind, $ref: target.$id });
312
+ }
313
+ else {
314
+ throw new Error('Type.Ref: Invalid arguments');
315
+ }
282
316
  }
283
317
  /** `Experimental` Creates a recursive type */
284
318
  Rec(callback, options = {}) {
285
319
  const $id = options.$id || '';
286
320
  const self = callback({ $ref: `${$id}#/$defs/self` });
287
- return { ...options, $ref: `${$id}#/$defs/self`, $defs: { self } };
288
- }
289
- /** `Experimental` Creates a recursive type. Pending https://github.com/ajv-validator/ajv/issues/1709 */
290
- // public Rec<T extends TProperties>($id: string, callback: (self: TAny) => T, options: ObjectOptions = {}): TObject<T> {
291
- // const properties = callback({ $recursiveRef: `${$id}` } as any)
292
- // return { ...options, kind: ObjectKind, $id, $recursiveAnchor: true, type: 'object', properties }
293
- // }
294
- /** `Experimental` Creates a namespace for a set of related types */
295
- Namespace($defs, options = {}) {
296
- return { ...options, kind: exports.BoxKind, $defs };
297
- }
298
- Ref(...args) {
299
- const $id = args[0]['$id'] || '';
300
- const key = args[1];
301
- return (args.length === 2) ? { $ref: `${$id}#/$defs/${key}` } : { $ref: $id };
321
+ return this.Store({ ...options, $ref: `${$id}#/$defs/self`, $defs: { self } });
322
+ }
323
+ /** Conditionally stores and schema if it contains an $id and returns */
324
+ Store(schema) {
325
+ const $schema = schema;
326
+ if (!$schema['$id'])
327
+ return $schema;
328
+ this.schemas.set($schema['$id'], $schema);
329
+ return $schema;
330
+ }
331
+ /** Conditionally dereferences a schema if RefKind. Otherwise return argument */
332
+ Deref(schema) {
333
+ const $schema = schema;
334
+ if ($schema['kind'] !== exports.RefKind)
335
+ return schema;
336
+ if (!this.schemas.has($schema['$ref']))
337
+ throw Error(`Unable to locate schema with $id '${$schema['$ref']}'`);
338
+ return this.Deref(this.schemas.get($schema['$ref']));
302
339
  }
303
340
  }
304
341
  exports.TypeBuilder = TypeBuilder;