Skip to content

Commit fa77c58

Browse files
chore: implement more validators
1 parent c678149 commit fa77c58

File tree

15 files changed

+323
-45
lines changed

15 files changed

+323
-45
lines changed

src/lib/isDecimal.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import type { DecimalOptions } from '../types'
1+
import type { DecimalValidatorOptions } from '../types'
22
import { decimal } from './alpha'
33
import assertString from './util/assertString'
44
import includes from './util/includesArray'
55
import merge from './util/merge'
66

7-
function decimalRegExp(options: DecimalOptions): RegExp {
7+
function decimalRegExp(options: DecimalValidatorOptions): RegExp {
88
const regExp = new RegExp(`^[-+]?([0-9]+)?(\\${decimal[options.locale]}[0-9]{${options.decimal_digits}})${options.force_decimal ? '' : '?'}$`)
99
return regExp
1010
}
@@ -24,9 +24,9 @@ const blacklist = ['', '-', '+']
2424
* @param options - Options object
2525
* @returns True if the string matches the validation, false otherwise
2626
*/
27-
export default function isDecimal(str: string, options: Partial<DecimalOptions>): boolean {
27+
export default function isDecimal(str: string, options: Partial<DecimalValidatorOptions>): boolean {
2828
assertString(str)
29-
const mergedOptions = merge(options, default_decimal_options) as DecimalOptions
29+
const mergedOptions = merge(options, default_decimal_options) as DecimalValidatorOptions
3030
if (mergedOptions.locale in decimal) {
3131
return !includes(blacklist, str.replace(/ /g, '')) && decimalRegExp(mergedOptions).test(str)
3232
}

src/lib/isFloat.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { IsFloatOptions } from '../types'
1+
import type { FloatOptions } from '../types'
22
import { decimal } from './alpha'
33
import assertString from './util/assertString'
44
import isNullOrUndefined from './util/nullUndefinedCheck'
@@ -10,7 +10,7 @@ import isNullOrUndefined from './util/nullUndefinedCheck'
1010
* @param options - Options object
1111
* @returns True if the string matches the validation, false otherwise
1212
*/
13-
export default function isFloat(str: string, options?: IsFloatOptions): boolean {
13+
export default function isFloat(str: string, options?: FloatOptions): boolean {
1414
assertString(str)
1515
const opts = options || {}
1616
const float = new RegExp(`^(?:[-+])?(?:[0-9]+)?(?:\\${typeof opts.locale === 'string' ? decimal[opts.locale] : '.'}[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$`)

src/types/base.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ export type ValidationNames = 'base' |
7171
'password' |
7272
'text' |
7373
'bigint' |
74-
'timestampTz'
74+
'timestampTz' |
75+
'float' |
76+
'decimal' |
77+
'time'
7578

7679
export type Infer<T> = T extends Validator<infer U> ? U : never

src/types/decimal.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type isDecimal from '../lib/isDecimal'
2+
import type { DecimalValidator } from '../validators/decimal'
3+
import type { LengthValidator, Validator } from './base'
4+
5+
export interface DecimalValidatorOptions {
6+
force_decimal: boolean
7+
decimal_digits: string
8+
locale: string
9+
}
10+
11+
export interface DecimalValidatorType extends Validator<number>, LengthValidator<DecimalValidator> {
12+
positive: () => DecimalValidator
13+
negative: () => DecimalValidator
14+
divisibleBy: (divisor: number) => DecimalValidator
15+
custom: (fn: (value: number) => boolean, message: string) => DecimalValidator
16+
}

src/types/float.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { FloatValidator } from '../validators/float'
2+
import type { LengthValidator, Validator } from './base'
3+
4+
export interface FloatOptions {
5+
locale?: string
6+
min?: number
7+
max?: number
8+
lt?: number
9+
gt?: number
10+
}
11+
12+
export interface FloatValidatorType extends Validator<number>, LengthValidator<FloatValidator> {
13+
positive: () => FloatValidator
14+
negative: () => FloatValidator
15+
divisibleBy: (divisor: number) => FloatValidator
16+
custom: (fn: (value: number) => boolean, message: string) => FloatValidator
17+
}

src/types/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ import type { ArrayValidatorType } from './array'
22
import type { BooleanValidatorType } from './boolean'
33
import type { CustomValidatorType } from './custom'
44
import type { DatetimeValidatorType, DateValidatorType } from './date'
5+
import type { DecimalValidatorType } from './decimal'
56
import type { EnumValidatorType } from './enum'
7+
import type { FloatValidatorType } from './float'
68
import type { BigintValidatorType, NumberValidatorType } from './number'
79
import type { ObjectValidatorType } from './object'
810
import type { PasswordValidatorType } from './password'
911
// Re-export ValidationInstance type
1012
import type { StringValidatorType, TextValidatorType } from './string'
13+
import type { TimeValidatorType } from './time'
1114
import type { TimestampValidatorType, UnixValidatorType } from './timestamp'
1215
import type { TimestampTzValidatorType } from './timestamptz'
1316

@@ -18,15 +21,18 @@ export * from './base'
1821
export * from './boolean'
1922
export * from './custom'
2023
export * from './date'
24+
export * from './decimal'
2125
export * from './enum'
26+
export * from './float'
2227
export * from './number'
2328
export * from './object'
2429
// Options types
2530
export * from './options'
31+
2632
export * from './password'
2733
// Validator types
2834
export * from './string'
29-
35+
export * from './time'
3036
export * from './timestamp'
3137
export * from './timestamptz'
3238

@@ -46,6 +52,9 @@ export interface ValidationInstance {
4652
timestampTz: () => TimestampTzValidatorType
4753
unix: () => UnixValidatorType
4854
password: () => PasswordValidatorType
55+
float: () => FloatValidatorType
56+
decimal: () => DecimalValidatorType
57+
time: () => TimeValidatorType
4958
}
5059

5160
export type ValidationType = {

src/types/number.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
1-
import type isDecimal from '../lib/isDecimal'
21
import type { NumberValidator } from '../validators/numbers'
32
import type { LengthValidator, Validator } from './base'
43

5-
export interface IsFloatOptions {
6-
locale?: string
7-
min?: number
8-
max?: number
9-
lt?: number
10-
gt?: number
11-
}
12-
134
export interface IsIntOptions {
145
allow_leading_zeroes?: boolean
156
min?: number
@@ -18,21 +9,13 @@ export interface IsIntOptions {
189
gt?: number
1910
}
2011

21-
export interface DecimalOptions {
22-
force_decimal: boolean
23-
decimal_digits: string
24-
locale: string
25-
}
26-
2712
export interface NumericOptions {
2813
no_symbols?: boolean
2914
locale?: string
3015
}
3116

3217
export interface NumberValidatorType extends Validator<number>, LengthValidator<NumberValidator> {
3318
integer: (options?: IsIntOptions) => NumberValidator
34-
float: (options?: IsFloatOptions) => NumberValidator
35-
decimal: (options?: Parameters<typeof isDecimal>[1]) => NumberValidator
3619
positive: () => NumberValidator
3720
negative: () => NumberValidator
3821
divisibleBy: (divisor: number) => NumberValidator

src/types/time.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { TimeValidator } from '../validators/time'
2+
import type { Validator } from './base'
3+
4+
export interface TimeValidatorType extends Validator<string> {
5+
min: (min: string) => TimeValidator
6+
max: (max: string) => TimeValidator
7+
length: (length: number) => TimeValidator
8+
custom: (fn: (value: string) => boolean, message: string) => TimeValidator
9+
}

src/validation.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@ import { boolean } from './validators/booleans'
55
import { custom } from './validators/custom'
66
import { date } from './validators/dates'
77
import { datetime } from './validators/datetimes'
8+
import { decimal } from './validators/decimal'
89
import { enum_ } from './validators/enums'
10+
import { float } from './validators/float'
911
import { number } from './validators/numbers'
1012
import { object } from './validators/objects'
1113
import { password } from './validators/password'
1214
import { string } from './validators/strings'
1315
import { text } from './validators/text'
16+
import { time } from './validators/time'
1417
import { timestamp } from './validators/timestamps'
18+
import { timestampTz } from './validators/timestamptz'
1519
import { unix } from './validators/unix'
1620

1721
export const v: ValidationInstance = {
@@ -27,6 +31,10 @@ export const v: ValidationInstance = {
2731
object,
2832
custom,
2933
timestamp,
34+
timestampTz,
3035
unix,
3136
password,
37+
float,
38+
decimal,
39+
time,
3240
}

src/validators/decimal.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import type { DecimalValidatorOptions, DecimalValidatorType, ValidationNames } from '../types'
2+
import isDecimal from '../lib/isDecimal'
3+
import isDivisibleBy from '../lib/isDivisibleBy'
4+
import { BaseValidator } from './base'
5+
6+
export class DecimalValidator extends BaseValidator<number> implements DecimalValidatorType {
7+
public name: ValidationNames = 'decimal'
8+
9+
constructor() {
10+
super()
11+
this.addRule({
12+
name: 'decimal',
13+
test: (value: unknown): value is number => {
14+
if (typeof value !== 'number' || Number.isNaN(value)) {
15+
return false
16+
}
17+
return isDecimal(String(value), {})
18+
},
19+
message: 'Must be a valid decimal number',
20+
})
21+
}
22+
23+
min(min: number): this {
24+
return this.addRule({
25+
name: 'min',
26+
test: (value: number) => value >= min,
27+
message: 'Must be at least {min}',
28+
params: { min },
29+
})
30+
}
31+
32+
max(max: number): this {
33+
return this.addRule({
34+
name: 'max',
35+
test: (value: number) => value <= max,
36+
message: 'Must be at most {max}',
37+
params: { max },
38+
})
39+
}
40+
41+
length(length: number): this {
42+
return this.addRule({
43+
name: 'length',
44+
test: (value: number) => value.toString().length === length,
45+
message: 'Must be exactly {length} digits',
46+
params: { length },
47+
})
48+
}
49+
50+
positive(): this {
51+
return this.addRule({
52+
name: 'positive',
53+
test: (value: number) => value > 0,
54+
message: 'Must be a positive number',
55+
})
56+
}
57+
58+
negative(): this {
59+
return this.addRule({
60+
name: 'negative',
61+
test: (value: number) => value < 0,
62+
message: 'Must be a negative number',
63+
})
64+
}
65+
66+
divisibleBy(divisor: number): this {
67+
return this.addRule({
68+
name: 'divisibleBy',
69+
test: (value: number) => isDivisibleBy(String(value), divisor),
70+
message: 'Must be divisible by {divisor}',
71+
params: { divisor },
72+
})
73+
}
74+
75+
custom(fn: (value: number) => boolean, message: string): this {
76+
return this.addRule({
77+
name: 'custom',
78+
test: fn,
79+
message,
80+
})
81+
}
82+
}
83+
84+
// Export a function to create decimal validators
85+
export function decimal(): DecimalValidator {
86+
return new DecimalValidator()
87+
}

0 commit comments

Comments
 (0)