Skip to content

Commit 2c48b18

Browse files
authored
Rework optionals to use undefined instead of null (#2)
1 parent 5b0392f commit 2c48b18

File tree

2 files changed

+51
-7
lines changed

2 files changed

+51
-7
lines changed

src/env.ts

+23-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ const parseValue = <T>(
77
const envVarName = declaration.variable ?? key
88
const envValue = env[envVarName]
99
if (typeof envValue === 'undefined') {
10-
if (typeof declaration.defaultValue === 'undefined') {
10+
if (!Object.prototype.hasOwnProperty.apply(declaration, ['defaultValue'])) {
1111
throw new Error(`No value specified for ${envVarName}`)
1212
}
13-
if (declaration.validator) declaration.validator(declaration.defaultValue)
13+
if (declaration.validator)
14+
declaration.validator(declaration.defaultValue as T)
1415
return [key, declaration.defaultValue]
1516
}
1617
try {
@@ -27,6 +28,26 @@ export function optional<T>({
2728
defaultValue,
2829
validator,
2930
...others
31+
}: Declaration<T>): Declaration<T | undefined> {
32+
return {
33+
parser,
34+
defaultValue: defaultValue || undefined,
35+
validator:
36+
validator &&
37+
((value: T | undefined) => {
38+
if (typeof value !== 'undefined') {
39+
validator(value)
40+
}
41+
}),
42+
...others,
43+
}
44+
}
45+
46+
export function nullable<T>({
47+
parser,
48+
defaultValue,
49+
validator,
50+
...others
3051
}: Declaration<T>): Declaration<T | null> {
3152
return {
3253
parser,

test/env.test.ts

+28-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { EnumVar } from '../src'
44
import { optional, TypedEnv } from '../src'
55
import { IntVar } from '../src'
66
import { StringVar } from '../src'
7+
import { nullable } from '../src/env'
78

89
describe('TypedEnv', () => {
910
test('basic parsing works', async () => {
@@ -110,15 +111,15 @@ describe('TypedEnv', () => {
110111
).toThrow('too small')
111112
})
112113

113-
test('handles optional variables', async () => {
114+
test('handles nullable variables', async () => {
114115
const rawEnv = {
115116
A: 'A',
116117
}
117118
expect(
118119
TypedEnv(
119120
{
120-
A: optional(StringVar()),
121-
B: optional(StringVar()),
121+
A: nullable(StringVar()),
122+
B: nullable(StringVar()),
122123
},
123124
rawEnv
124125
)
@@ -137,9 +138,9 @@ describe('TypedEnv', () => {
137138
delete process.env.SOME_RANDOM_VAR_NAME
138139
})
139140

140-
test("Validators don't fire for nulls on optionals", async () => {
141+
test("Validators don't fire for nulls on nullables", async () => {
141142
const schema = {
142-
A: optional(
143+
A: nullable(
143144
IntVar({
144145
validator() {
145146
throw new Error('nope')
@@ -152,4 +153,26 @@ describe('TypedEnv', () => {
152153
expect(() => TypedEnv(schema, rawEnv)).toThrow('nope')
153154
expect(TypedEnv(schema, emptyEnv).A).toBeNull()
154155
})
156+
157+
test('Optionals return undefined instead of null', async () => {
158+
const schema = {
159+
A: optional(StringVar()),
160+
B: optional(
161+
StringVar({
162+
validator() {
163+
throw new Error('nope')
164+
},
165+
})
166+
),
167+
C: optional(
168+
StringVar({
169+
validator() {
170+
return true
171+
},
172+
})
173+
),
174+
}
175+
const emptyEnv = { C: 'foo' }
176+
expect(TypedEnv(schema, emptyEnv).A).toBeUndefined()
177+
})
155178
})

0 commit comments

Comments
 (0)