Skip to content

Commit 9126796

Browse files
authored
feat: add optionalWhen variant (#4)
1 parent d4192cb commit 9126796

File tree

4 files changed

+129
-0
lines changed

4 files changed

+129
-0
lines changed

src/schema/boolean.ts

+17
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,20 @@ boolean.optional = function optionalBoolean(options?: SchemaFnOptions) {
5151
return castToBoolean(key, value, options?.message)
5252
}
5353
}
54+
55+
/**
56+
* Same as the optional rule, but allows a condition to decide when to
57+
* validate the value
58+
*/
59+
boolean.optionalWhen = function optionalWhenBoolean(
60+
condition: boolean | ((key: string, value?: string) => boolean),
61+
options?: SchemaFnOptions
62+
) {
63+
return function validate(key: string, value?: string): boolean | undefined {
64+
if (typeof condition === 'function' ? condition(key, value) : condition) {
65+
return boolean.optional(options)(key, value)
66+
}
67+
68+
return boolean(options)(key, value)
69+
}
70+
}

src/schema/number.ts

+17
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,20 @@ number.optional = function optionalNumber(options?: SchemaFnOptions) {
4848
return castToNumber(key, value, options?.message)
4949
}
5050
}
51+
52+
/**
53+
* Same as the optional rule, but allows a condition to decide when to
54+
* validate the value
55+
*/
56+
number.optionalWhen = function optionalWhenNumber(
57+
condition: boolean | ((key: string, value?: string) => boolean),
58+
options?: SchemaFnOptions
59+
) {
60+
return function validate(key: string, value?: string): number | undefined {
61+
if (typeof condition === 'function' ? condition(key, value) : condition) {
62+
return number.optional(options)(key, value)
63+
}
64+
65+
return number(options)(key, value)
66+
}
67+
}

src/schema/string.ts

+17
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,20 @@ string.optional = function optionalString(options?: StringFnOptions) {
8282
return value
8383
}
8484
}
85+
86+
/**
87+
* Same as the optional rule, but allows a condition to decide when to
88+
* validate the value
89+
*/
90+
string.optionalWhen = function optionalWhenString(
91+
condition: boolean | ((key: string, value?: string) => boolean),
92+
options?: StringFnOptions
93+
) {
94+
return function validate(key: string, value?: string): string | undefined {
95+
if (typeof condition === 'function' ? condition(key, value) : condition) {
96+
return string.optional(options)(key, value)
97+
}
98+
99+
return string(options)(key, value)
100+
}
101+
}

tests/schema.spec.ts

+78
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,32 @@ test.group('schema | number.optional', () => {
8080
expectTypeOf(value).toEqualTypeOf<number | undefined>()
8181
assert.deepEqual(value, -22.198)
8282
})
83+
84+
test('allow conditional optional', ({ assert, expectTypeOf }) => {
85+
const value = schema.number.optionalWhen(true)('PORT')
86+
expectTypeOf(value).toEqualTypeOf<number | undefined>()
87+
assert.isUndefined(value)
88+
89+
const value2 = schema.number.optionalWhen(false)('PORT', '22')
90+
expectTypeOf(value2).toEqualTypeOf<number | undefined>()
91+
assert.deepEqual(value2, 22)
92+
93+
const fn = () => schema.number.optionalWhen(false)('PORT')
94+
assert.throws(fn, 'Missing environment variable "PORT"')
95+
})
96+
97+
test('allow conditional optional with function', ({ assert, expectTypeOf }) => {
98+
const value = schema.number.optionalWhen(() => true)('PORT')
99+
expectTypeOf(value).toEqualTypeOf<number | undefined>()
100+
assert.isUndefined(value)
101+
102+
const value2 = schema.number.optionalWhen(() => false)('PORT', '22')
103+
expectTypeOf(value2).toEqualTypeOf<number | undefined>()
104+
assert.deepEqual(value2, 22)
105+
106+
const fn = () => schema.number.optionalWhen(() => false)('PORT')
107+
assert.throws(fn, 'Missing environment variable "PORT"')
108+
})
83109
})
84110

85111
test.group('schema | string', () => {
@@ -154,6 +180,32 @@ test.group('schema | string.optional', () => {
154180
'mailgun:1234/v1'
155181
)
156182
})
183+
184+
test('allow conditional optional', ({ assert, expectTypeOf }) => {
185+
const value = schema.string.optionalWhen(true)('PORT')
186+
expectTypeOf(value).toEqualTypeOf<string | undefined>()
187+
assert.deepEqual(value, undefined)
188+
189+
const value2 = schema.string.optionalWhen(false)('PORT', '22')
190+
expectTypeOf(value2).toEqualTypeOf<string | undefined>()
191+
assert.deepEqual(value2, '22')
192+
193+
const fn = () => schema.string.optionalWhen(false)('PORT')
194+
assert.throws(fn, 'Missing environment variable "PORT"')
195+
})
196+
197+
test('allow conditional optional with function', ({ assert, expectTypeOf }) => {
198+
const value = schema.string.optionalWhen(() => true)('PORT')
199+
expectTypeOf(value).toEqualTypeOf<string | undefined>()
200+
assert.deepEqual(value, undefined)
201+
202+
const value2 = schema.string.optionalWhen(() => false)('PORT', '22')
203+
expectTypeOf(value2).toEqualTypeOf<string | undefined>()
204+
assert.deepEqual(value2, '22')
205+
206+
const fn = () => schema.string.optionalWhen(() => false)('PORT')
207+
assert.throws(fn, 'Missing environment variable "PORT"')
208+
})
157209
})
158210

159211
test.group('schema | boolean', () => {
@@ -238,6 +290,32 @@ test.group('schema | boolean.optional', () => {
238290
expectTypeOf(value).toEqualTypeOf<boolean | undefined>()
239291
assert.deepEqual(value, false)
240292
})
293+
294+
test('allow conditional optional', ({ assert, expectTypeOf }) => {
295+
const value = schema.boolean.optionalWhen(true)('PORT')
296+
expectTypeOf(value).toEqualTypeOf<boolean | undefined>()
297+
assert.deepEqual(value, undefined)
298+
299+
const value2 = schema.boolean.optionalWhen(false)('PORT', 'true')
300+
expectTypeOf(value2).toEqualTypeOf<boolean | undefined>()
301+
assert.deepEqual(value2, true)
302+
303+
const fn = () => schema.boolean.optionalWhen(false)('PORT')
304+
assert.throws(fn, 'Missing environment variable "PORT"')
305+
})
306+
307+
test('allow conditional optional with function', ({ assert, expectTypeOf }) => {
308+
const value = schema.boolean.optionalWhen(() => true)('PORT')
309+
expectTypeOf(value).toEqualTypeOf<boolean | undefined>()
310+
assert.deepEqual(value, undefined)
311+
312+
const value2 = schema.boolean.optionalWhen(() => false)('PORT', 'true')
313+
expectTypeOf(value2).toEqualTypeOf<boolean | undefined>()
314+
assert.deepEqual(value2, true)
315+
316+
const fn = () => schema.boolean.optionalWhen(() => false)('PORT')
317+
assert.throws(fn, 'Missing environment variable "PORT"')
318+
})
241319
})
242320

243321
test.group('schema | enum', () => {

0 commit comments

Comments
 (0)