Skip to content

Commit 8a89294

Browse files
authored
feat: Serialize the empty string to undefined (#22)
BREAKING CHANGE: Serialization of the empty string is now treated as `undefined` instead of an UnserializableParamError. Otherwise, the serialization of `{ foo: null }` would conflict with `{ foo: '' }`. This serializer chooses to support the more common and more useful case of sending `null` as a meaningful value while treating the empty string as sending no value. This aligns with common UX patterns where input fields are initialized to the empty string. Closes #20.
1 parent 9dc0ea9 commit 8a89294

File tree

3 files changed

+13
-15
lines changed

3 files changed

+13
-15
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ which encodes most non-alphanumeric characters.
2727
- Any `undefined` values are removed,
2828
e.g., `{ foo: undefined, bar: 1 }` serializes to `bar=1`.
2929
- The primitive type `string` is serialized using `.toString()`.
30-
- Serialization of the empty string
31-
is not supported and will throw an `UnserializableParamError`.
30+
- Serialization of the empty string is treated as `undefined`.
3231
Otherwise, the serialization of `{ foo: null }` would conflict with `{ foo: '' }`.
33-
This serializer chooses to support the more common and more useful case of `null`.
32+
This serializer chooses to support the more common and more useful case of sending `null`
33+
as a meaningful value while treating the empty string as sending no value.
34+
This aligns with common UX patterns where input fields are initialized to the empty string.
3435
- The primitive `number` and `bigint` types are serialized using `.toString()`.
3536
- The primitive `boolean` type is serialized using `.toString()`,
3637
e.g., `{ foo: true, bar: false }` serializes to `foo=true&bar=false`.

src/lib/serialize.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ const nestedUpdateUrlSearchParams = (
4242
continue
4343
}
4444

45+
if (typeof value === 'string' && value.length === 0) {
46+
continue
47+
}
48+
4549
if (Array.isArray(value)) {
4650
if (value.length === 0) {
4751
searchParams.set(name, '')
@@ -79,12 +83,6 @@ const nestedUpdateUrlSearchParams = (
7983
const serialize = (k: string, v: unknown): string => {
8084
if (v === null) return ''
8185
if (typeof v === 'string') {
82-
if (v.length === 0) {
83-
throw new UnserializableParamError(
84-
k,
85-
'is the empty string which is unsupported',
86-
)
87-
}
8886
return v.toString()
8987
}
9088
if (typeof v === 'number') {

test/serialization.test.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ test('serializes string', (t) => {
1717
t.is(serializeUrlSearchParams({ foo: '0' }), 'foo=0')
1818
})
1919

20+
test('serializes the empty string to undefined', (t) => {
21+
t.is(serializeUrlSearchParams({ foo: '' }), '')
22+
t.is(serializeUrlSearchParams({ foo: 'd', bar: '' }), 'foo=d')
23+
})
24+
2025
test('serializes number', (t) => {
2126
t.is(serializeUrlSearchParams({ foo: 1 }), 'foo=1')
2227
t.is(serializeUrlSearchParams({ foo: 23.8 }), 'foo=23.8')
@@ -173,12 +178,6 @@ test('cannot serialize non-plain objects', (t) => {
173178
})
174179
})
175180

176-
test('cannot serialize the empty string', (t) => {
177-
t.throws(() => serializeUrlSearchParams({ foo: '' }), {
178-
instanceOf: UnserializableParamError,
179-
})
180-
})
181-
182181
test('cannot serialize array params with unserializable values', (t) => {
183182
t.throws(() => serializeUrlSearchParams({ foo: [''] }), {
184183
instanceOf: UnserializableParamError,

0 commit comments

Comments
 (0)