@kaliber/forms 2.1.2 → 3.0.0-beta.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/package.json +13 -1
- package/src/components.js +21 -1
- package/src/components.type.test.ts +74 -0
- package/src/fields.js +98 -16
- package/src/fields.type.test.ts +135 -0
- package/src/hooks.js +65 -7
- package/src/hooks.type.test.ts +164 -0
- package/src/normalize.js +78 -25
- package/src/normalize.type.test.ts +125 -0
- package/src/schema.js +39 -11
- package/src/schema.type.test.ts +159 -0
- package/src/snapshot.js +59 -5
- package/src/snapshot.type.test.ts +135 -0
- package/src/state.js +31 -1
- package/src/type-helpers.js +12 -0
- package/src/type.test.helpers.ts +47 -0
- package/src/types.ts +354 -0
- package/src/validation.js +96 -12
- package/validation.js +1 -1
- package/src/types.d.ts +0 -15
package/src/types.ts
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* While type declarations should be opaque, type hinting in visual studio still shows them, this
|
|
3
|
+
* is used to make the types from this library, exposed to the developer more friendly.
|
|
4
|
+
*/
|
|
5
|
+
export type Expand<T> =
|
|
6
|
+
T extends infer O ? { [K in keyof O]: O[K] } : never
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Typescript will (sometimes) forget the keys are strings when using `keyof T`, so this is
|
|
10
|
+
* Partial for keys that we need to be strings.
|
|
11
|
+
*/
|
|
12
|
+
export type PartialWithStringKey<T extends { [key: string]: any}> =
|
|
13
|
+
{
|
|
14
|
+
[K in keyof T & string]?:
|
|
15
|
+
T[K] extends (infer U)[] ? (
|
|
16
|
+
U extends object ? PartialWithStringKey<U>[] : T[K]
|
|
17
|
+
) :
|
|
18
|
+
T[K] extends object ? (
|
|
19
|
+
PartialWithStringKey<T[K]>
|
|
20
|
+
) :
|
|
21
|
+
T[K]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type Falsy = false | '' | 0 | 0n | null | undefined | void
|
|
25
|
+
|
|
26
|
+
/** Equivalent of `array.map(x => x[prop])` for tuples */
|
|
27
|
+
export type MapTuple<T extends [...unknown[]], prop extends string> =
|
|
28
|
+
T extends [infer X extends { [key in prop]: unknown }, ...infer Rest]
|
|
29
|
+
? [X[prop], ...MapTuple<Rest, prop>]
|
|
30
|
+
: []
|
|
31
|
+
|
|
32
|
+
export type ValidateToValue<R> =
|
|
33
|
+
R extends ValidationFunction<infer T> ? T :
|
|
34
|
+
R extends readonly [] | null ? unknown :
|
|
35
|
+
R extends readonly [ValidationFunction<infer T>] ? T :
|
|
36
|
+
R extends readonly [ValidationFunction<infer T>, ...infer Rest] ? T & ValidateToValue<Rest> :
|
|
37
|
+
never
|
|
38
|
+
|
|
39
|
+
export type Validate<T = any> =
|
|
40
|
+
readonly [ValidationFunction<T>, ...ValidationFunction<T>[]] |
|
|
41
|
+
ValidationFunction<T> |
|
|
42
|
+
readonly ValidationFunction<T>[] |
|
|
43
|
+
null
|
|
44
|
+
export type ValidationFunction<T = any> = (value: T, context: ValidationContext) => ValidationResult
|
|
45
|
+
export type ValidationResult = Falsy | ValidationError
|
|
46
|
+
export type ValidationError = { id: string, params?: any[] }
|
|
47
|
+
export type ValidationContext = { form: any, parents: Field.Object[] }
|
|
48
|
+
|
|
49
|
+
export type InitialValue<T extends FieldInput.Object> =
|
|
50
|
+
PartialWithStringKey<NormalizedField.ToValue<NormalizedInitialValue<T>>>
|
|
51
|
+
|
|
52
|
+
export type NormalizedInitialValue<T extends FieldInput.Object> =
|
|
53
|
+
NormalizedField.FromFieldSchema<FieldSchema.Object<T>>
|
|
54
|
+
|
|
55
|
+
export namespace FieldInput {
|
|
56
|
+
|
|
57
|
+
export type ArrayToValue<T extends FieldInput.Array> =
|
|
58
|
+
FieldSchema.ToValue<FieldSchema.Array<T>>
|
|
59
|
+
|
|
60
|
+
export type ObjectToValue<T extends FieldInput.Object> =
|
|
61
|
+
FieldSchema.ToValue<FieldSchema.Object<T>>
|
|
62
|
+
|
|
63
|
+
export type Object = FieldSchema.ObjectFields
|
|
64
|
+
|
|
65
|
+
export type Array = Object | HeterogeneousArrayInput
|
|
66
|
+
export type HeterogeneousArrayInput = (initialValue: unknown) => Object
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type FieldSchema =
|
|
70
|
+
Validate |
|
|
71
|
+
FieldSchema.Object |
|
|
72
|
+
FieldSchema.Array |
|
|
73
|
+
{ validate: Validate }
|
|
74
|
+
|
|
75
|
+
export namespace FieldSchema {
|
|
76
|
+
|
|
77
|
+
export type ToValue<T extends FieldSchema> =
|
|
78
|
+
T extends Object<infer X> ? ObjectFieldsToValues<X> :
|
|
79
|
+
T extends Array<infer X> ? ArrayFieldsToValues<X> :
|
|
80
|
+
T extends { validate: infer V } ? (
|
|
81
|
+
V extends Validate ? ValidateToValue<V> : ValidateToValue<T>) :
|
|
82
|
+
ValidateToValue<T>
|
|
83
|
+
|
|
84
|
+
export type ObjectFieldsToValues<T extends ObjectFields> =
|
|
85
|
+
T extends any ? { [K in keyof T & string]: ToValue<T[K]> } : never
|
|
86
|
+
|
|
87
|
+
export type ArrayFieldsToValues<T extends ArrayFields> =
|
|
88
|
+
ObjectFieldsToValues<ExtractObjectFields<T>>[]
|
|
89
|
+
|
|
90
|
+
export type ExtractObjectFields<T extends ArrayFields> =
|
|
91
|
+
T extends FieldSchema.ObjectFields ? T :
|
|
92
|
+
T extends FieldSchema.ArrayFieldsConstructor ? ReturnType<T> :
|
|
93
|
+
never
|
|
94
|
+
|
|
95
|
+
export type Object<T extends ObjectFields = ObjectFields> = {
|
|
96
|
+
type: 'object',
|
|
97
|
+
fields: T,
|
|
98
|
+
validate?: Validate,
|
|
99
|
+
}
|
|
100
|
+
export type ObjectFields = {
|
|
101
|
+
[name: string]: FieldSchema
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type Array<T extends ArrayFields = ArrayFields> = {
|
|
105
|
+
type: 'array',
|
|
106
|
+
fields: T,
|
|
107
|
+
validate?: Validate,
|
|
108
|
+
}
|
|
109
|
+
export type ArrayFields = FieldSchema.ObjectFields | ArrayFieldsConstructor
|
|
110
|
+
export type ArrayFieldsConstructor = (initialValues: any) => FieldSchema.ObjectFields
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export type NormalizedField = NormalizedField.Basic | NormalizedField.Object | NormalizedField.Array
|
|
114
|
+
|
|
115
|
+
export namespace NormalizedField {
|
|
116
|
+
export type ToValue<T extends NormalizedField> =
|
|
117
|
+
T extends Object<infer X> ? FieldSchema.ObjectFieldsToValues<X> :
|
|
118
|
+
T extends Array<infer X> ? FieldSchema.ArrayFieldsToValues<X> :
|
|
119
|
+
T extends Basic<infer X> ? X :
|
|
120
|
+
never
|
|
121
|
+
|
|
122
|
+
export type FromFieldSchema<T extends FieldSchema> =
|
|
123
|
+
T extends FieldSchema.Object ? (
|
|
124
|
+
{
|
|
125
|
+
type: 'object',
|
|
126
|
+
validate: T extends { validate: any } ? ValidateToValidationFunction<T['validate']> : null,
|
|
127
|
+
fields: T['fields']
|
|
128
|
+
}
|
|
129
|
+
) :
|
|
130
|
+
T extends FieldSchema.Array ? (
|
|
131
|
+
{
|
|
132
|
+
type: 'array',
|
|
133
|
+
validate: T extends { validate: any } ? ValidateToValidationFunction<T['validate']> : null,
|
|
134
|
+
fields: T['fields']
|
|
135
|
+
}
|
|
136
|
+
) :
|
|
137
|
+
T extends null ? (
|
|
138
|
+
{
|
|
139
|
+
type: 'basic',
|
|
140
|
+
validate: null
|
|
141
|
+
}
|
|
142
|
+
) :
|
|
143
|
+
T extends Validate<any> ? (
|
|
144
|
+
{
|
|
145
|
+
type: 'basic',
|
|
146
|
+
validate: ValidateToValidationFunction<T>
|
|
147
|
+
}
|
|
148
|
+
) :
|
|
149
|
+
T extends { validate: infer X, type?: Exclude<infer Y, 'object' | 'array'> } ? (
|
|
150
|
+
{
|
|
151
|
+
type: 'basic',
|
|
152
|
+
validate: ValidateToValidationFunction<X>
|
|
153
|
+
}
|
|
154
|
+
) :
|
|
155
|
+
never
|
|
156
|
+
|
|
157
|
+
export type ValidateToValidationFunction<T> =
|
|
158
|
+
T extends undefined ? null :
|
|
159
|
+
T extends null ? null :
|
|
160
|
+
T extends Validate ? ValidationFunction<ValidateToValue<T>> :
|
|
161
|
+
never
|
|
162
|
+
|
|
163
|
+
export type Basic<T = unknown> = {
|
|
164
|
+
type: 'basic',
|
|
165
|
+
validate: null | ValidationFunction<T>,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export type Object<T extends FieldSchema.ObjectFields = FieldSchema.ObjectFields> = {
|
|
169
|
+
type: 'object',
|
|
170
|
+
validate: null | ValidationFunction<FieldSchema.ObjectFieldsToValues<T>>,
|
|
171
|
+
fields: T,
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export type Array<T extends FieldSchema.ArrayFields = FieldSchema.ArrayFields> = {
|
|
175
|
+
type: 'array',
|
|
176
|
+
validate: null | ValidationFunction<FieldSchema.ArrayFieldsToValues<T>>,
|
|
177
|
+
fields: T,
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export type Field = Field.Basic | Field.Object | Field.Array
|
|
182
|
+
|
|
183
|
+
export namespace Field {
|
|
184
|
+
export type ToValue<T extends Field> =
|
|
185
|
+
T extends Object<infer X> ? ObjectFieldsToValues<X> :
|
|
186
|
+
T extends Array<infer X> ? ObjectFieldsToValues<X>[] :
|
|
187
|
+
T extends Basic<infer X> ? X :
|
|
188
|
+
never
|
|
189
|
+
|
|
190
|
+
export type ObjectFieldsToValues<T extends ObjectFields> =
|
|
191
|
+
T extends any ? { [K in keyof T & string]: ToValue<T[K]> } : never
|
|
192
|
+
|
|
193
|
+
export type ObjectFromObjectInput<T extends FieldInput.Object> =
|
|
194
|
+
FromNormalizedField<NormalizedField.FromFieldSchema<FieldSchema.Object<T>>>
|
|
195
|
+
|
|
196
|
+
export type FromNormalizedField<T extends NormalizedField> =
|
|
197
|
+
T extends NormalizedField.Object<infer X> ? Object<ObjectFieldsToFields<X>> :
|
|
198
|
+
T extends NormalizedField.Array<infer X> ? Array<ArrayFieldsToObjectFields<X>> :
|
|
199
|
+
T extends NormalizedField.Basic<infer X> ? Basic<X> :
|
|
200
|
+
never
|
|
201
|
+
|
|
202
|
+
export type ObjectFieldsToFields<T extends FieldSchema.ObjectFields> =
|
|
203
|
+
T extends any
|
|
204
|
+
? { [K in keyof T & string]: FromNormalizedField<NormalizedField.FromFieldSchema<T[K]>>}
|
|
205
|
+
: never
|
|
206
|
+
|
|
207
|
+
export type ArrayFieldsToObjectFields<T extends FieldSchema.ArrayFields> =
|
|
208
|
+
FromNormalizedField<
|
|
209
|
+
NormalizedField.FromFieldSchema<
|
|
210
|
+
FieldSchema.Object<FieldSchema.ExtractObjectFields<T>>
|
|
211
|
+
>
|
|
212
|
+
>['fields']
|
|
213
|
+
|
|
214
|
+
export type BaseFieldProperties<T extends 'object' | 'array' | 'basic'> = {
|
|
215
|
+
type: T,
|
|
216
|
+
name: string,
|
|
217
|
+
validate(context: ValidationContext): void,
|
|
218
|
+
setSubmitted(isSubmitted: boolean): void,
|
|
219
|
+
reset(): void,
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export type Basic<T = any> =
|
|
223
|
+
BaseFieldProperties<'basic'> &
|
|
224
|
+
{
|
|
225
|
+
value: State.ReadonlyWithHistory<T>,
|
|
226
|
+
state: State.ReadonlyWithHistory<State.Basic<T>>,
|
|
227
|
+
eventHandlers: {
|
|
228
|
+
onBlur(): void,
|
|
229
|
+
onFocus(): void,
|
|
230
|
+
onChange(eOrValue: Event<T> | T): void,
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export type Event<T = any> = {
|
|
235
|
+
target: {
|
|
236
|
+
value?: T
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export type Object<T extends ObjectFields = ObjectFields> =
|
|
241
|
+
BaseFieldProperties<'object'> &
|
|
242
|
+
{
|
|
243
|
+
value: State.Readonly<ObjectFieldsToValues<T>>,
|
|
244
|
+
state: State.ReadonlyWithHistory<State.Object>,
|
|
245
|
+
fields: T,
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export type Array<T extends ObjectFields = ObjectFields> =
|
|
249
|
+
BaseFieldProperties<'array'> &
|
|
250
|
+
{
|
|
251
|
+
value: State.Readonly<ObjectFieldsToValues<T>[]>,
|
|
252
|
+
state: State.ReadonlyWithHistory<State.Array<Object<T>>>,
|
|
253
|
+
helpers: {
|
|
254
|
+
add(initialValue: PartialWithStringKey<ObjectFieldsToValues<T>>): void,
|
|
255
|
+
remove(entry: Object<T>): void,
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export type ObjectFields = { [name: string]: Field }
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export type State = State.Basic | State.Object | State.Array
|
|
263
|
+
|
|
264
|
+
export namespace State {
|
|
265
|
+
export type Common = {
|
|
266
|
+
isSubmitted: boolean,
|
|
267
|
+
isVisited: boolean,
|
|
268
|
+
hasFocus: boolean,
|
|
269
|
+
} & (Valid | Invalid)
|
|
270
|
+
|
|
271
|
+
export type Valid = {
|
|
272
|
+
error: Falsy,
|
|
273
|
+
invalid: false,
|
|
274
|
+
showError: false,
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export type Invalid = {
|
|
278
|
+
error: ValidationError,
|
|
279
|
+
invalid: true,
|
|
280
|
+
showError: boolean,
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export type Basic<T = any> = Common & {
|
|
284
|
+
value: T
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export type Object = Common
|
|
288
|
+
|
|
289
|
+
export type Array<T = Field.Object> = Common & {
|
|
290
|
+
children: T[]
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export type Readonly<T = any> = {
|
|
294
|
+
get(): T,
|
|
295
|
+
subscribe(f: Subscription<T>): Unsubscribe,
|
|
296
|
+
}
|
|
297
|
+
export type ReadonlyWithHistory<T = any> = {
|
|
298
|
+
get(): T,
|
|
299
|
+
subscribe(f: SubscriptionWithHistory<T>): Unsubscribe,
|
|
300
|
+
}
|
|
301
|
+
export type ReadWrite<T = any> = ReadonlyWithHistory<T> & {
|
|
302
|
+
update(f: (oldValue: T) => T): T
|
|
303
|
+
}
|
|
304
|
+
export type Subscription<T> = (newValue: T) => void
|
|
305
|
+
export type SubscriptionWithHistory<T> = (newValue: T, oldValue: T) => void
|
|
306
|
+
export type Unsubscribe = () => void
|
|
307
|
+
|
|
308
|
+
export type StateTupleToValueTuple<T extends [...unknown[]]> =
|
|
309
|
+
T extends [State.Readonly<infer X>, ...infer Rest]
|
|
310
|
+
? [X, ...StateTupleToValueTuple<Rest>]
|
|
311
|
+
: []
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export type Snapshot = Snapshot.Basic | Snapshot.Object | Snapshot.Array
|
|
315
|
+
|
|
316
|
+
export namespace Snapshot {
|
|
317
|
+
export type FromField<T extends Field> =
|
|
318
|
+
T extends Field.Object<infer X> ? Object<X> :
|
|
319
|
+
T extends Field.Array<infer X> ? Array<X> :
|
|
320
|
+
T extends Field.Basic<infer X> ? Basic<X> :
|
|
321
|
+
never
|
|
322
|
+
|
|
323
|
+
export type FromObjectFields<T extends FieldSchema.ObjectFields> =
|
|
324
|
+
Snapshot.Object<Field.ObjectFieldsToFields<T>>
|
|
325
|
+
|
|
326
|
+
export type Basic<T = any> =
|
|
327
|
+
Pick<State.Basic<T>, 'value' | 'invalid' | 'error'>
|
|
328
|
+
|
|
329
|
+
export type Object<T extends Field.ObjectFields = Field.ObjectFields> =
|
|
330
|
+
{
|
|
331
|
+
invalid: boolean,
|
|
332
|
+
value: MapToValues<T>,
|
|
333
|
+
error: {
|
|
334
|
+
self: Falsy | ValidationError,
|
|
335
|
+
children: MapToErrors<T>,
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export type Array<T extends Field.ObjectFields = Field.ObjectFields> =
|
|
340
|
+
{
|
|
341
|
+
invalid: boolean,
|
|
342
|
+
value: MapToValues<T>[],
|
|
343
|
+
error: {
|
|
344
|
+
self: Falsy | ValidationError,
|
|
345
|
+
children: Object<T>['error'][],
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export type MapToValues<T extends Field.ObjectFields> =
|
|
350
|
+
T extends any ? { [K in keyof T & string]: FromField<T[K]>['value'] } : never
|
|
351
|
+
|
|
352
|
+
export type MapToErrors<T extends Field.ObjectFields> =
|
|
353
|
+
T extends any ? { [K in keyof T & string]: FromField<T[K]>['error'] } : never
|
|
354
|
+
}
|
package/src/validation.js
CHANGED
|
@@ -1,22 +1,106 @@
|
|
|
1
|
+
/** @import { Falsy, ValidationFunction } from './types.ts' */
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @template {string | Constructable<any>} T
|
|
6
|
+
* @typedef {(
|
|
7
|
+
* T extends Constructable<infer R> ? R :
|
|
8
|
+
* T extends string ? TypeLookup<T> :
|
|
9
|
+
* never
|
|
10
|
+
* )} TypeFromLookupOrConstructable
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @template T
|
|
15
|
+
* @typedef {abstract new (...args: any) => T} Constructable
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @template {string} T
|
|
20
|
+
* @typedef {(
|
|
21
|
+
* T extends 'string' ? string :
|
|
22
|
+
* T extends 'number' ? number :
|
|
23
|
+
* T extends `${infer A} | ${infer B}` ? TypeLookup<A> | TypeLookup<B> :
|
|
24
|
+
* T extends 'boolean' ? boolean :
|
|
25
|
+
* T extends `${infer X}[]` ? TypeLookup<X>[] :
|
|
26
|
+
* never
|
|
27
|
+
* )} TypeLookup
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @template T
|
|
32
|
+
* @param {T} type
|
|
33
|
+
* @returns {Constructable<T>}
|
|
34
|
+
*/
|
|
35
|
+
export function withT(type) {
|
|
36
|
+
return /** @type {any} */(null)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @template const T
|
|
41
|
+
*
|
|
42
|
+
* @overload
|
|
43
|
+
* @arg {T} type
|
|
44
|
+
* @returns {(x: TypeFromLookupOrConstructable<T> | undefined) => never}
|
|
45
|
+
*
|
|
46
|
+
* @arg {any} type
|
|
47
|
+
* @returns {null | ((x: any) => never)}
|
|
48
|
+
*/
|
|
49
|
+
export function optionalT(type) {
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @template const T
|
|
55
|
+
*
|
|
56
|
+
* @arg {T} type
|
|
57
|
+
* @returns {ValidationFunction<TypeFromLookupOrConstructable<T> >}
|
|
58
|
+
*/
|
|
59
|
+
export function requiredT(type) {
|
|
60
|
+
return required
|
|
61
|
+
}
|
|
62
|
+
|
|
1
63
|
export const optional = null
|
|
2
|
-
export const required =
|
|
64
|
+
export const required =
|
|
65
|
+
/** @template T @arg {T} x */
|
|
66
|
+
x => !x && x !== false && x !== 0 && error('required')
|
|
67
|
+
|
|
68
|
+
export const number =
|
|
69
|
+
/** @arg {number} x */
|
|
70
|
+
x => Number(x) !== x && error('number')
|
|
71
|
+
|
|
72
|
+
/** @arg {number} min */
|
|
73
|
+
export function min(min) {
|
|
74
|
+
/** @arg {number} x */
|
|
75
|
+
return x => (x || x === 0) && x < min && error('min', min)
|
|
76
|
+
}
|
|
3
77
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
78
|
+
/** @arg {number} max */
|
|
79
|
+
export function max(max) {
|
|
80
|
+
/** @arg {number} x */
|
|
81
|
+
return x => (x || x === 0) && x > max && error('max', max)
|
|
82
|
+
}
|
|
9
83
|
|
|
10
|
-
/** @
|
|
11
|
-
export function minLength(min) {
|
|
12
|
-
/** @
|
|
13
|
-
|
|
84
|
+
/** @arg {number} min */
|
|
85
|
+
export function minLength(min) {
|
|
86
|
+
/** @arg {{ length: number }} x */
|
|
87
|
+
return x => x && x.length < min && error('minLength', min)
|
|
88
|
+
}
|
|
89
|
+
/** @arg {number} max */
|
|
90
|
+
export function maxLength(max) {
|
|
91
|
+
/** @arg {{ length: number }} x */
|
|
92
|
+
return x => x && x.length > max && error('maxLength', max)
|
|
93
|
+
}
|
|
14
94
|
|
|
15
95
|
const emailRegex = /.+@.+\..+/
|
|
16
|
-
export const email =
|
|
96
|
+
export const email =
|
|
97
|
+
/** @arg {string} x */
|
|
98
|
+
x => x && !emailRegex.test(x) && error('email')
|
|
17
99
|
|
|
18
100
|
/**
|
|
19
101
|
* @template {string} T
|
|
20
|
-
* @
|
|
102
|
+
* @template {[...any]} P
|
|
103
|
+
* @arg {T} id
|
|
104
|
+
* @arg {P} params
|
|
21
105
|
*/
|
|
22
106
|
export function error(id, ...params) { return { id, params } }
|
package/validation.js
CHANGED
package/src/types.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// This file exists because you can not do conditional types in jsdoc
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* If you have an optional parameter which is generic, it is inferred as any, the IfAny helps to
|
|
5
|
-
* detect that.
|
|
6
|
-
*
|
|
7
|
-
* When T === any return Y else return N
|
|
8
|
-
*/
|
|
9
|
-
type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* While type declarations should be opaque, type hinting in visual studio still shows them, this
|
|
13
|
-
* is used to make the types from this library, exposed to the developer more friendly.
|
|
14
|
-
*/
|
|
15
|
-
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never
|