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