• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pelotom / runtypes / 7191708659

13 Dec 2023 06:46AM UTC coverage: 95.97%. First build
7191708659

Pull #339

github

web-flow
Merge 03ef182ec into f0b4cfd74
Pull Request #339: rafactor!: revise project configurations

311 of 329 branches covered (0.0%)

Branch coverage included in aggregate %.

1213 of 1259 new or added lines in 40 files covered. (96.35%)

1213 of 1259 relevant lines covered (96.35%)

1743.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.06
/src/Runtype.ts
1
import Brand from "./Brand.ts"
10✔
2
import Constraint, { ConstraintCheck } from "./Constraint.ts"
10✔
3
import Intersect from "./Intersect.ts"
10✔
4
import Literal from "./Literal.ts"
10✔
5
import Optional from "./Optional.ts"
10✔
6
import Union from "./Union.ts"
10✔
7
import Result from "./result/Result.ts"
8
import ValidationError from "./result/ValidationError.ts"
10✔
9
import Reflect from "./utils/Reflect.ts"
10
import Static from "./utils/Static.ts"
11
import SUCCESS from "./utils-internal/SUCCESS.ts"
10✔
12
import hasKey from "./utils-internal/hasKey.ts"
10✔
13
import show from "./utils-internal/show.ts"
10✔
14

15
const RuntypeSymbol: unique symbol = Symbol()
10✔
16

17
const isRuntype = (x: any): x is RuntypeBase<unknown> => hasKey(RuntypeSymbol, x)
10✔
18

19
/**
20
 * A runtype determines at runtime whether a value conforms to a type specification.
21
 */
22
interface RuntypeBase<A = unknown> {
23
        /**
24
         * Verifies that a value conforms to this runtype. When given a value that does
25
         * not conform to the runtype, throws an exception.
26
         */
27
        assert(x: any): asserts x is A
28

29
        /**
30
         * Verifies that a value conforms to this runtype. If so, returns the same value,
31
         * statically typed. Otherwise throws an exception.
32
         */
33
        check(x: any): A
34

35
        /**
36
         * Validates that a value conforms to this type, and returns a result indicating
37
         * success or failure (does not throw).
38
         */
39
        validate(x: any): Result<A>
40

41
        /**
42
         * A type guard for this runtype.
43
         */
44
        guard(x: any): x is A
45

46
        /**
47
         * Convert this to a Reflect, capable of introspecting the structure of the type.
48
         */
49
        readonly reflect: Reflect
50

51
        /* @internal */ readonly _falseWitness: A
52
        /* @internal */ readonly [RuntypeSymbol]: true
53
}
54

55
/**
56
 * A runtype determines at runtime whether a value conforms to a type specification.
57
 */
58
interface Runtype<A = unknown> extends RuntypeBase<A> {
59
        /**
60
         * Union this Runtype with another.
61
         */
62
        Or<B extends RuntypeBase>(B: B): Union<[this, B]>
63

64
        /**
65
         * Intersect this Runtype with another.
66
         */
67
        And<B extends RuntypeBase>(B: B): Intersect<[this, B]>
68

69
        /**
70
         * Optionalize this Runtype.
71
         */
72
        optional(): Optional<this>
73

74
        /**
75
         * Union this Runtype with `Null`.
76
         */
77
        nullable(): Union<[this, Literal<null>]>
78

79
        /**
80
         * Use an arbitrary constraint function to validate a runtype, and optionally
81
         * to change its name and/or its static type.
82
         *
83
         * @template T - Optionally override the static type of the resulting runtype
84
         * @param {(x: Static<this>) => boolean | string} constraint - Custom function
85
         * that returns `true` if the constraint is satisfied, `false` or a custom
86
         * error message if not.
87
         * @param [options]
88
         * @param {string} [options.name] - allows setting the name of this
89
         * constrained runtype, which is helpful in reflection or diagnostic
90
         * use-cases.
91
         */
92
        withConstraint<T extends Static<this>, K = unknown>(
93
                constraint: ConstraintCheck<this>,
94
                options?: { name?: string; args?: K },
95
        ): Constraint<this, T, K>
96

97
        /**
98
         * Helper function to convert an underlying Runtype into another static type
99
         * via a type guard function.  The static type of the runtype is inferred from
100
         * the type of the guard function.
101
         *
102
         * @template T - Typically inferred from the return type of the type guard
103
         * function, so usually not needed to specify manually.
104
         * @param {(x: Static<this>) => x is T} guard - Type guard function (see
105
         * https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)
106
         *
107
         * @param [options]
108
         * @param {string} [options.name] - allows setting the name of this
109
         * constrained runtype, which is helpful in reflection or diagnostic
110
         * use-cases.
111
         */
112
        withGuard<T extends Static<this>, K = unknown>(
113
                guard: (x: Static<this>) => x is T,
114
                options?: { name?: string; args?: K },
115
        ): Constraint<this, T, K>
116

117
        /**
118
         * Adds a brand to the type.
119
         */
120
        withBrand<B extends string>(brand: B): Brand<B, this>
121
}
122

123
class FunctionExtensible<F extends Function> {
10✔
NEW
124
        constructor(f: F) {
×
NEW
125
                return Object.setPrototypeOf(f, new.target.prototype)
×
NEW
126
        }
×
127
}
10✔
128

129
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
1✔
130
class Runtype<A = unknown, T = A>
10✔
131
        extends FunctionExtensible<(...args: any) => void>
10✔
132
        implements Runtype<A, T> {}
10✔
133

134
const create = <A extends RuntypeBase>(
10✔
135
        validate: (x: any, visited: VisitedState) => Result<Static<A>>,
10✔
136
        base: any,
10✔
137
): A => {
138
        const check = (x: any) => {
458✔
139
                const result: Result<unknown> = base.validate(x)
507✔
140
                if (result.success) return result.value
507✔
141
                else throw new ValidationError(result)
591✔
142
        }
458✔
143

144
        const guard = (x: any): x is A => base.validate(x).success
458✔
145

146
        const Or = <B extends Runtype>(B: B): Union<[A, B]> => Union(base, B)
458✔
147

148
        const And = <B extends Runtype>(B: B): Intersect<[A, B]> => Intersect(base, B)
458✔
149

150
        const optional = (): Optional<A> => Optional(base)
458✔
151

152
        const nullable = (): Union<[A, Literal<null>]> => Union(base, Literal(null))
458✔
153

154
        const withConstraint = <T extends Static<A>, K = unknown>(
458✔
155
                constraint: ConstraintCheck<A>,
458✔
156
                options?: { name?: string; args?: K },
458✔
157
        ): Constraint<A, T, K> => Constraint<A, T, K>(base, constraint, options)
458✔
158

159
        const withGuard = <T extends Static<A>, K = unknown>(
458✔
160
                guard: (x: Static<A>) => x is T,
458✔
161
                options?: { name?: string; args?: K },
458✔
162
        ): Constraint<A, T, K> => Constraint<A, T, K>(base, guard, options)
458✔
163

164
        const withBrand = <B extends string>(B: B): Brand<B, A> => Brand(B, base)
458✔
165

166
        base[RuntypeSymbol] = true
458✔
167
        base.check = check
458✔
168
        base.assert = check
458✔
169
        base._innerValidate = (value: any, visited: VisitedState) => {
458✔
170
                if (visited.has(value, base)) return SUCCESS(value)
7,574✔
171
                return validate(value, visited)
14,661✔
172
        }
458✔
173
        base.validate = (value: any) => base._innerValidate(value, VisitedState())
458✔
174
        base.guard = guard
458✔
175
        base.Or = Or
458✔
176
        base.And = And
458✔
177
        base.optional = optional
458✔
178
        base.nullable = nullable
458✔
179
        base.withConstraint = withConstraint
458✔
180
        base.withGuard = withGuard
458✔
181
        base.withBrand = withBrand
458✔
182
        base.reflect = base
458✔
183
        base.toString = () => `Runtype<${show(base)}>`
458✔
184

185
        return base
458✔
186
}
10✔
187

188
const innerValidate = <A extends RuntypeBase>(
10✔
189
        targetType: A,
10✔
190
        value: any,
10✔
191
        visited: VisitedState,
10✔
192
): Result<Static<A>> => (targetType as any)._innerValidate(value, visited)
10✔
193

194
type VisitedState = {
195
        has: (candidate: object, type: RuntypeBase) => boolean
196
}
197
const VisitedState = (): VisitedState => {
10✔
198
        const members: WeakMap<object, WeakMap<RuntypeBase, true>> = new WeakMap()
5,148✔
199

200
        const add = (candidate: unknown, type: RuntypeBase) => {
5,148✔
201
                if (candidate === null || !(typeof candidate === "object")) return
12,264✔
202
                const typeSet = members.get(candidate)
23,381✔
203
                members.set(
12,264✔
204
                        candidate,
12,264✔
205
                        typeSet
12,264✔
206
                                ? typeSet.set(type, true)
12,264✔
207
                                : (new WeakMap() as WeakMap<RuntypeBase, true>).set(type, true),
12,264✔
208
                )
209
        }
5,148✔
210

211
        const has = (candidate: object, type: RuntypeBase) => {
5,148✔
212
                const typeSet = members.get(candidate)
12,264✔
213
                const value = (typeSet && typeSet.get(type)) || false
12,264✔
214
                add(candidate, type)
12,264✔
215
                return value
12,264✔
216
        }
5,148✔
217

218
        return { has }
15,444✔
219
}
10✔
220

221
export default Runtype // eslint-disable-next-line import/no-named-export
20✔
222
export { type RuntypeBase, VisitedState, create, innerValidate, isRuntype }
10✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc