@jakobkg/shapes-ts 0.2.0 → 0.3.1

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.
package/README.md CHANGED
@@ -2,12 +2,15 @@
2
2
 
3
3
  Toy library to create runtime safe types in TS.
4
4
 
5
- When describing the shape of your data in shapes-ts, you get the corresponding TS type and a validation utility for free!
5
+ When describing the shape of your data in shapes-ts, you get the corresponding
6
+ TS type and a validation utility for free!
6
7
 
7
8
  ```typescript
8
- const Role = Shapes.object({
9
- role: Shapes.string(),
10
- actor: Shapes.string(),
9
+ const CreditCategory = Shapes.enum(["director", "writer", "actor/actress"])
10
+
11
+ const Credits = Shapes.object({
12
+ category: CreditCategory,
13
+ name: Shapes.string(),
11
14
  });
12
15
 
13
16
  const Movie = Shapes.object({
@@ -16,14 +19,17 @@ const Movie = Shapes.object({
16
19
  (n) => n >= 0 && n <= 5,
17
20
  "rating must be in range 0.0-5.0",
18
21
  ),
19
- cast: Shapes.array(Role),
22
+ credits: Shapes.array(Credits),
20
23
  });
21
24
 
22
25
  // This infers as
23
26
  // {
24
27
  // title: string,
25
28
  // rating: number,
26
- // cast: { role: string, actor: string }[]
29
+ // credits: {
30
+ // category: "director" | "writer" | "actor/actress",
31
+ // name: string
32
+ // }[]
27
33
  // }
28
34
  type Movie = Shapes.Type<typeof Movie>;
29
35
 
@@ -47,19 +53,28 @@ for (const member of movieData.cast) {
47
53
  - string
48
54
  - number
49
55
  - boolean
56
+
57
+ ...and literals of these
58
+
59
+ ### Composite types
60
+
50
61
  - object
51
62
  - array
63
+ - tuple
64
+ - enum
52
65
 
53
66
  ### Modified types
54
67
 
55
68
  - Nullable types (`T | null`)
56
69
  - Optional types (`T | undefined`)
57
- - Unions (`A & B`)
58
- - Intersections (`A | B`)
70
+ - Unions (`A | B`)
71
+ - Intersections (`A & B`)
59
72
 
60
73
  ## Predicates
61
74
 
62
- Shapes can have additional validation checks added to them. These do not affect the inferred type, but are still ran as part of a shape's `.check()` method to help enforce constraints that are not easily modeled using the type system.
75
+ Shapes can have additional validation checks added to them. These do not affect
76
+ the inferred type, but are still ran as part of a shape's `.check()` method to
77
+ help enforce constraints that are not easily modeled using the type system.
63
78
 
64
79
  ```typescript
65
80
  const Username = Shapes.string().where(
@@ -71,7 +86,9 @@ Username.check("jakob :)"); // true
71
86
  Username.check("CptAmericaFan207"); // false
72
87
  ```
73
88
 
74
- Predicates can be used with all types of shapes, and they may be combined both by stacking them on a single shape and by combining shapes that already have predicates on them.
89
+ Predicates can be used with all types of shapes, and they may be combined both
90
+ by stacking them on a single shape and by combining shapes that already have
91
+ predicates on them.
75
92
 
76
93
  ```typescript
77
94
  const Fizz = Shapes.number().where(
package/dist/shapes.d.ts CHANGED
@@ -1,15 +1,16 @@
1
1
  declare const Predicates: unique symbol;
2
- declare const Optional: unique symbol;
3
2
  declare const Properties: unique symbol;
4
3
  declare const AllowUnknownProperties: unique symbol;
5
4
  /**
6
5
  * Bridge to convert a Shape into a TS type.
7
6
  */
8
7
  export type Type<T> = T extends Shape<infer U> ? U : never;
9
- type ShapePredicate<T> = (x: T) => boolean;
8
+ type ShapePredicate<T> = {
9
+ predicate: (x: T) => boolean;
10
+ description: string | undefined;
11
+ };
10
12
  interface Shape<T> {
11
13
  readonly typename: string;
12
- readonly [Optional]?: boolean;
13
14
  readonly [Predicates]: ShapePredicate<T>[];
14
15
  check(x: unknown): x is T;
15
16
  /**
@@ -18,9 +19,9 @@ interface Shape<T> {
18
19
  * @param description Optional description of the constraint for error messages
19
20
  * @returns A new shape with the additional validation
20
21
  */
21
- where(predicate: ShapePredicate<T>, description: string): Shape<T>;
22
- and<U>(other: Shape<U>): IntersectionShape<T, U>;
23
- or<U>(other: Shape<U>): UnionShape<T, U>;
22
+ where(predicate: (x: T) => boolean, description: string | undefined): Shape<T>;
23
+ and<U>(other: Shape<U>): Shape<T & U>;
24
+ or<U>(other: Shape<U>): Shape<T | U>;
24
25
  }
25
26
  interface ObjectShape<T = Record<string, unknown>> extends Shape<T> {
26
27
  readonly [Properties]: Record<string, Shape<T>>;
@@ -30,23 +31,6 @@ interface ObjectShapeOptions {
30
31
  readonly allowUnknownProperties?: boolean;
31
32
  readonly additionalPermittedProperties?: Record<string, Shape<Record<string, unknown>>>;
32
33
  }
33
- interface ArrayShape<T> extends Shape<Array<T>> {
34
- readonly inner: Shape<T>;
35
- }
36
- interface UnionShape<Left, Right> extends Shape<Left | Right> {
37
- left: Shape<Left>;
38
- right: Shape<Right>;
39
- }
40
- interface IntersectionShape<Left, Right> extends Shape<Left & Right> {
41
- left: Shape<Left>;
42
- right: Shape<Right>;
43
- }
44
- interface OptionalShape<T> extends Shape<T | undefined> {
45
- readonly inner: Shape<T>;
46
- }
47
- interface NullableShape<T> extends Shape<T | null> {
48
- readonly inner: Shape<T>;
49
- }
50
34
  /**
51
35
  * Creates a shape representing a JS `number`.
52
36
  * Does not support BigInt.
@@ -60,13 +44,21 @@ export declare function string(): Shape<string>;
60
44
  * Creates a shape representing a JS `boolean`.
61
45
  */
62
46
  export declare function boolean(): Shape<boolean>;
63
- type Primitive = number | string | boolean | object | null | undefined;
64
- export declare function literal<X extends Primitive, T extends Primitive extends X ? never : X>(x: X): Shape<T>;
47
+ type Primitive = number | string | boolean | null | undefined;
48
+ /**
49
+ * Creates a shape representing a primitive literal type.
50
+ * Takes numbers, strings, booleans, null and undefined.
51
+ *
52
+ * The resulting shape is equivalent to the type you would
53
+ * get when doing something like `const one = 1 as const;`,
54
+ * where the type is just the literal `1`
55
+ */
56
+ export declare function literal<T extends Primitive>(x: T): Shape<T>;
65
57
  /**
66
58
  * Creates an array shape, representing `Array<T>` (aka `T[]`).
67
59
  * Takes the shape representing `T` as a parameter.
68
60
  */
69
- export declare function array<T>(shape: Shape<T>): ArrayShape<T>;
61
+ export declare function array<T>(shape: Shape<T>): Shape<T[]>;
70
62
  /**
71
63
  * Creates an object shape.
72
64
  *
@@ -89,15 +81,31 @@ export declare function array<T>(shape: Shape<T>): ArrayShape<T>;
89
81
  export declare function object<T extends Record<string, Shape<any>>>(properties: T, options?: ObjectShapeOptions): ObjectShape<{
90
82
  [K in keyof T]: Type<T[K]>;
91
83
  }>;
84
+ /**
85
+ * Creates a shape representing a tuple type, with an optional rest
86
+ *
87
+ * Shapes.tuple([Shapes.number(), Shape.string()]) is equivalent to TS type [number, string]
88
+ * Shapes.tuple([Shapes.number()], Shapes.string()) is equivalent to TS type [number, ...string]
89
+ */
90
+ export declare function tuple<Shapes extends Shape<any>[], Partial extends {
91
+ [K in keyof Shapes]: Type<Shapes[K]>;
92
+ }, Tuple extends RestShape extends Shape<infer Rest> ? [...Partial, ...Rest[]] : [...Partial], RestShape extends Shape<any> | undefined = undefined>(shapes: [...Shapes], rest?: RestShape): Shape<Tuple>;
93
+ export { enumOf as enum };
94
+ /**
95
+ * Creates a shape representing an enum type
96
+ * (Not an actual enum as made using the enum keyword)
97
+ *
98
+ * `Shapes.enum(["a", "b", "c"])` is equivalent to `"a" | "b" | "c"`
99
+ */
100
+ declare function enumOf<T extends (string | number)[]>(values: [...T]): Shape<T[number]>;
92
101
  /**
93
102
  * Creates a shape representing an optional type.
94
103
  * `Shapes.optional(T)` corresponds to `T | undefined`
95
104
  */
96
- export declare function optional<T>(shape: Shape<T>): OptionalShape<T>;
105
+ export declare function optional<T>(shape: Shape<T>): Shape<T | undefined>;
97
106
  /**
98
107
  * Creates a shape representing a nullable type.
99
108
  * `Shapes.nullable(T)` corresponds to `T | null`
100
109
  */
101
- export declare function nullable<T>(shape: Shape<T>): NullableShape<T>;
102
- export {};
110
+ export declare function nullable<T>(shape: Shape<T>): Shape<T | null>;
103
111
  //# sourceMappingURL=shapes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shapes.d.ts","sourceRoot":"","sources":["../src/shapes/shapes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shapes.d.ts","sourceRoot":"","sources":["../src/shapes/shapes.ts"],"names":[],"mappings":"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"}
package/dist/shapes.js CHANGED
@@ -26,12 +26,13 @@ exports.boolean = boolean;
26
26
  exports.literal = literal;
27
27
  exports.array = array;
28
28
  exports.object = object;
29
+ exports.tuple = tuple;
30
+ exports.enum = enumOf;
29
31
  exports.optional = optional;
30
32
  exports.nullable = nullable;
31
33
  /// SYMBOLS
32
34
  // These are here to hide some API bits from consumers
33
35
  var Predicates = Symbol();
34
- var Optional = Symbol();
35
36
  var Properties = Symbol();
36
37
  var AllowUnknownProperties = Symbol();
37
38
  /// TYPE GUARDS
@@ -49,9 +50,6 @@ function isObject(input) {
49
50
  function isArray(input) {
50
51
  return Array.isArray(input);
51
52
  }
52
- function isNull(input) {
53
- return input === null;
54
- }
55
53
  function isBoolean(input) {
56
54
  return typeof input === "boolean";
57
55
  }
@@ -61,17 +59,16 @@ function isUndefined(input) {
61
59
  function makeUnion(left, right) {
62
60
  var _a;
63
61
  return _a = {
64
- typename: "".concat(left.typename, " | ").concat(right.typename),
65
- left: left,
66
- right: right
62
+ typename: "".concat(left.typename, " | ").concat(right.typename)
67
63
  },
68
64
  _a[Predicates] = [],
69
- _a.where = function (predicate) {
70
- return __assign(__assign({}, this), { predicates: __spreadArray(__spreadArray([], this[Predicates], true), [predicate], false) });
65
+ _a.where = function (predicate, description) {
66
+ var _a;
67
+ return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [{ predicate: predicate, description: description }], false), _a));
71
68
  },
72
69
  _a.check = function (x) {
73
70
  return (left.check(x) || right.check(x)) &&
74
- this[Predicates].every(function (p) { return p(x); });
71
+ this[Predicates].every(function (p) { return p.predicate(x); });
75
72
  },
76
73
  _a.and = function (other) {
77
74
  return makeIntersection(this, other);
@@ -82,20 +79,22 @@ function makeUnion(left, right) {
82
79
  _a;
83
80
  }
84
81
  function makeIntersection(left, right) {
85
- var _a, _b, _c;
82
+ var _a;
86
83
  return _a = {
87
- typename: "".concat(left.typename, " | ").concat(right.typename),
88
- left: __assign(__assign({}, left), (_b = {}, _b[Properties] = __assign(__assign({}, right[Properties]), left[Properties]), _b)),
89
- right: __assign(__assign({}, right), (_c = {}, _c[Properties] = __assign(__assign({}, left[Properties]), right[Properties]), _c))
84
+ typename: "".concat(left.typename, " & ").concat(right.typename)
90
85
  },
91
86
  _a[Predicates] = [],
92
- _a.where = function (predicate) {
87
+ _a.where = function (predicate, description) {
93
88
  var _a;
94
- return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [predicate], false), _a));
89
+ return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [{ predicate: predicate, description: description }], false), _a));
95
90
  },
96
91
  _a.check = function (x) {
97
- return this.left.check(x) && this.right.check(x) &&
98
- this[Predicates].every(function (p) { return p(x); });
92
+ var _a, _b;
93
+ // Trickery: combine permitted properties of left and right
94
+ var tolerantLeft = __assign(__assign({}, left), (_a = {}, _a[Properties] = __assign(__assign({}, right[Properties]), left[Properties]), _a));
95
+ var tolerantRight = __assign(__assign({}, right), (_b = {}, _b[Properties] = __assign(__assign({}, left[Properties]), right[Properties]), _b));
96
+ return tolerantLeft.check(x) && tolerantRight.check(x) &&
97
+ this[Predicates].every(function (p) { return p.predicate(x); });
99
98
  },
100
99
  _a.and = function (other) {
101
100
  return makeIntersection(this, other);
@@ -117,11 +116,11 @@ function number() {
117
116
  },
118
117
  _a[Predicates] = [],
119
118
  _a.check = function (x) {
120
- return isNumber(x) && this[Predicates].every(function (p) { return p(x); });
119
+ return isNumber(x) && this[Predicates].every(function (p) { return p.predicate(x); });
121
120
  },
122
- _a.where = function (predicate) {
121
+ _a.where = function (predicate, description) {
123
122
  var _a;
124
- return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [predicate], false), _a));
123
+ return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [{ predicate: predicate, description: description }], false), _a));
125
124
  },
126
125
  _a.and = function (other) {
127
126
  return makeIntersection(this, other);
@@ -139,13 +138,13 @@ function string() {
139
138
  return _a = {
140
139
  typename: "string",
141
140
  check: function (x) {
142
- return isString(x) && this[Predicates].every(function (p) { return p(x); });
141
+ return isString(x) && this[Predicates].every(function (p) { return p.predicate(x); });
143
142
  }
144
143
  },
145
144
  _a[Predicates] = [],
146
- _a.where = function (predicate) {
145
+ _a.where = function (predicate, description) {
147
146
  var _a;
148
- return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [predicate], false), _a));
147
+ return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [{ predicate: predicate, description: description }], false), _a));
149
148
  },
150
149
  _a.and = function (other) {
151
150
  return makeIntersection(this, other);
@@ -163,13 +162,13 @@ function boolean() {
163
162
  return _a = {
164
163
  typename: "boolean",
165
164
  check: function (x) {
166
- return isBoolean(x) && this[Predicates].every(function (p) { return p(x); });
165
+ return isBoolean(x) && this[Predicates].every(function (p) { return p.predicate(x); });
167
166
  }
168
167
  },
169
168
  _a[Predicates] = [],
170
- _a.where = function (predicate) {
169
+ _a.where = function (predicate, description) {
171
170
  var _a;
172
- return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [predicate], false), _a));
171
+ return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [{ predicate: predicate, description: description }], false), _a));
173
172
  },
174
173
  _a.and = function (other) {
175
174
  return makeIntersection(this, other);
@@ -179,18 +178,30 @@ function boolean() {
179
178
  },
180
179
  _a;
181
180
  }
181
+ /**
182
+ * Creates a shape representing a primitive literal type.
183
+ * Takes numbers, strings, booleans, null and undefined.
184
+ *
185
+ * The resulting shape is equivalent to the type you would
186
+ * get when doing something like `const one = 1 as const;`,
187
+ * where the type is just the literal `1`
188
+ */
182
189
  function literal(x) {
183
190
  var _a;
191
+ function matchesLiteral(u) {
192
+ return u === x;
193
+ }
184
194
  return _a = {
185
- typename: JSON.stringify(x)
195
+ typename: isUndefined(x) ? "undefined" : JSON.stringify(x)
186
196
  },
187
197
  _a[Predicates] = [],
188
- _a.check = function (input) {
189
- return input === x && this[Predicates].every(function (p) { return p(input); });
198
+ _a.check = function (x) {
199
+ return matchesLiteral(x) &&
200
+ this[Predicates].every(function (p) { return p.predicate(x); });
190
201
  },
191
- _a.where = function (predicate) {
202
+ _a.where = function (predicate, description) {
192
203
  var _a;
193
- return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [predicate], false), _a));
204
+ return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [{ predicate: predicate, description: description }], false), _a));
194
205
  },
195
206
  _a.and = function (other) {
196
207
  return makeIntersection(this, other);
@@ -210,23 +221,18 @@ function array(shape) {
210
221
  var typename = "Array<".concat(shape.typename, ">");
211
222
  return _a = {
212
223
  typename: typename,
213
- inner: shape,
214
- check: function (input) {
215
- if (!isArray(input)) {
216
- return false;
224
+ check: function (x) {
225
+ function innerMatches(x) {
226
+ return x.every(function (entry) { return shape.check(entry); });
217
227
  }
218
- return input.every(function (entry) {
219
- if (!shape.check(entry)) {
220
- return false;
221
- }
222
- return true;
223
- }) && this[Predicates].every(function (p) { return p(input); });
228
+ return isArray(x) && innerMatches(x) &&
229
+ this[Predicates].every(function (p) { return p.predicate(x); });
224
230
  }
225
231
  },
226
232
  _a[Predicates] = [],
227
- _a.where = function (predicate) {
233
+ _a.where = function (predicate, description) {
228
234
  var _a;
229
- return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [predicate], false), _a));
235
+ return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [{ predicate: predicate, description: description }], false), _a));
230
236
  },
231
237
  _a.and = function (other) {
232
238
  return makeIntersection(this, other);
@@ -258,44 +264,52 @@ function array(shape) {
258
264
  // deno-lint-ignore no-explicit-any
259
265
  function object(properties, options) {
260
266
  var _a;
261
- var allowUnknownProperties = options === null || options === void 0 ? void 0 : options.allowUnknownProperties;
267
+ var _b;
268
+ var allowUnknownProperties = (_b = options === null || options === void 0 ? void 0 : options.allowUnknownProperties) !== null && _b !== void 0 ? _b : false;
262
269
  return _a = {
263
270
  typename: "Object"
264
271
  },
265
272
  _a[AllowUnknownProperties] = allowUnknownProperties,
266
- _a.check = function (input) {
267
- // Check that input is an object
268
- if (!isObject(input)) {
269
- return false;
270
- }
271
- // Check for unknown properties
272
- if (!this[AllowUnknownProperties]) {
273
- for (var inputProperty in input) {
274
- if (!(inputProperty in this[Properties])) {
275
- return false;
276
- }
277
- }
278
- }
279
- // Check required properties and their types
280
- for (var property in this[Properties]) {
281
- var field = input[property];
282
- var propertyShape = this[Properties][property];
283
- // Property presence check
284
- if (!(property in input) && !propertyShape[Optional]) {
273
+ _a.check = function (x) {
274
+ var _this = this;
275
+ var matchesShape = function (input) {
276
+ // Check that input is an object
277
+ if (!isObject(input)) {
285
278
  return false;
286
279
  }
287
- // Property type check
288
- if (!propertyShape.check(field)) {
289
- return false;
280
+ // Check for unknown properties
281
+ if (!_this[AllowUnknownProperties]) {
282
+ for (var inputProperty in input) {
283
+ if (!(inputProperty in _this[Properties])) {
284
+ return false;
285
+ }
286
+ }
290
287
  }
291
- }
292
- return this[Predicates].every(function (p) { return p(input); });
288
+ // Check required properties and their types
289
+ for (var property in _this[Properties]) {
290
+ var field = input[property];
291
+ var propertyShape = _this[Properties][property];
292
+ if (isUndefined(propertyShape))
293
+ return false;
294
+ // Property presence check
295
+ if (!(property in input) && !propertyShape.check(undefined)) {
296
+ return false;
297
+ }
298
+ // Property type check
299
+ if (!propertyShape.check(field)) {
300
+ return false;
301
+ }
302
+ }
303
+ return true;
304
+ };
305
+ return matchesShape(x) &&
306
+ this[Predicates].every(function (p) { return p.predicate(x); });
293
307
  },
294
308
  _a[Properties] = __assign(__assign({}, properties), options === null || options === void 0 ? void 0 : options.additionalPermittedProperties),
295
309
  _a[Predicates] = [],
296
- _a.where = function (predicate) {
310
+ _a.where = function (predicate, description) {
297
311
  var _a;
298
- return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [predicate], false), _a));
312
+ return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [{ predicate: predicate, description: description }], false), _a));
299
313
  },
300
314
  _a.and = function (other) {
301
315
  return makeIntersection(this, other);
@@ -305,26 +319,36 @@ function object(properties, options) {
305
319
  },
306
320
  _a;
307
321
  }
308
- /// TYPE MODIFIERS
309
322
  /**
310
- * Creates a shape representing an optional type.
311
- * `Shapes.optional(T)` corresponds to `T | undefined`
323
+ * Creates a shape representing a tuple type, with an optional rest
324
+ *
325
+ * Shapes.tuple([Shapes.number(), Shape.string()]) is equivalent to TS type [number, string]
326
+ * Shapes.tuple([Shapes.number()], Shapes.string()) is equivalent to TS type [number, ...string]
312
327
  */
313
- function optional(shape) {
328
+ function tuple(shapes, rest) {
314
329
  var _a;
330
+ var typename = "[".concat(shapes.map(function (s) { return s.typename; }).join(", "), "]");
315
331
  return _a = {
316
- typename: "".concat(shape.typename, " | undefined"),
317
- inner: shape
332
+ typename: typename
318
333
  },
319
- _a[Optional] = true,
320
334
  _a[Predicates] = [],
321
- _a.check = function (input) {
322
- return (isUndefined(input) || shape.check(input)) &&
323
- this[Predicates].every(function (p) { return p(input); });
335
+ _a.check = function (x) {
336
+ if (!isArray(x))
337
+ return false;
338
+ if (x.length > shapes.length) {
339
+ // Oversized x and no rest shape can't be valid
340
+ if (rest === undefined)
341
+ return false;
342
+ for (var idx = shapes.length; idx < x.length; idx++) {
343
+ if (!rest.check(x[idx]))
344
+ return false;
345
+ }
346
+ }
347
+ return shapes.every(function (shape, idx) { return shape.check(x[idx]); }) && this[Predicates].every(function (p) { return p.predicate(x); });
324
348
  },
325
- _a.where = function (predicate) {
349
+ _a.where = function (predicate, description) {
326
350
  var _a;
327
- return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [predicate], false), _a));
351
+ return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [{ predicate: predicate, description: description }], false), _a));
328
352
  },
329
353
  _a.and = function (other) {
330
354
  return makeIntersection(this, other);
@@ -335,23 +359,23 @@ function optional(shape) {
335
359
  _a;
336
360
  }
337
361
  /**
338
- * Creates a shape representing a nullable type.
339
- * `Shapes.nullable(T)` corresponds to `T | null`
362
+ * Creates a shape representing an enum type
363
+ * (Not an actual enum as made using the enum keyword)
364
+ *
365
+ * `Shapes.enum(["a", "b", "c"])` is equivalent to `"a" | "b" | "c"`
340
366
  */
341
- function nullable(shape) {
367
+ function enumOf(values) {
342
368
  var _a;
343
- var nullShape = (_a = {
344
- typename: "".concat(shape.typename, " | null"),
345
- check: function (input) {
346
- return (isNull(input) || shape.check(input)) &&
347
- this[Predicates].every(function (p) { return p(input); });
348
- },
349
- inner: shape
369
+ return _a = {
370
+ typename: "".concat(values.map(function (v) { return JSON.stringify(v); }).join(" | "))
350
371
  },
351
372
  _a[Predicates] = [],
352
- _a.where = function (predicate) {
373
+ _a.check = function (x) {
374
+ return (isNumber(x) || isString(x)) && values.includes(x) && this[Predicates].every(function (p) { return p.predicate(x); });
375
+ },
376
+ _a.where = function (predicate, description) {
353
377
  var _a;
354
- return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [predicate], false), _a));
378
+ return __assign(__assign({}, this), (_a = {}, _a[Predicates] = __spreadArray(__spreadArray([], this[Predicates], true), [{ predicate: predicate, description: description }], false), _a));
355
379
  },
356
380
  _a.and = function (other) {
357
381
  return makeIntersection(this, other);
@@ -359,6 +383,20 @@ function nullable(shape) {
359
383
  _a.or = function (other) {
360
384
  return makeUnion(this, other);
361
385
  },
362
- _a);
363
- return nullShape;
386
+ _a;
387
+ }
388
+ /// TYPE MODIFIERS
389
+ /**
390
+ * Creates a shape representing an optional type.
391
+ * `Shapes.optional(T)` corresponds to `T | undefined`
392
+ */
393
+ function optional(shape) {
394
+ return makeUnion(shape, literal(undefined));
395
+ }
396
+ /**
397
+ * Creates a shape representing a nullable type.
398
+ * `Shapes.nullable(T)` corresponds to `T | null`
399
+ */
400
+ function nullable(shape) {
401
+ return makeUnion(shape, literal(null));
364
402
  }
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "homepage": "https://codeberg.org/jakobkg/shapes-ts#readme",
8
8
  "license": "BSD-3-Clause",
9
- "version": "0.2.0",
9
+ "version": "0.3.1",
10
10
  "private": false,
11
11
  "main": "dist/index.js",
12
12
  "scripts": {