Skip to content

Commit 84c5189

Browse files
authored
fix(serializer/types): Allow null value in serializer for parsers with default values (#769)
Allow passing a `null` value for a key where the parser has a default value, to clear it. Note that it won't write the default value in the output string, it will only clear the key if it was present in the base, like the hooks would.
1 parent 1c46a8e commit 84c5189

File tree

4 files changed

+34
-7
lines changed

4 files changed

+34
-7
lines changed

packages/nuqs/src/parsers.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -421,13 +421,13 @@ export function parseAsArrayOf<ItemType>(
421421
}
422422

423423
type inferSingleParserType<Parser> = Parser extends ParserBuilder<
424-
infer Type
424+
infer Value
425425
> & {
426-
defaultValue: infer Type
426+
defaultValue: infer Value
427427
}
428-
? Type
429-
: Parser extends ParserBuilder<infer Type>
430-
? Type | null
428+
? Value
429+
: Parser extends ParserBuilder<infer Value>
430+
? Value | null
431431
: never
432432

433433
type inferParserRecordType<Map extends Record<string, ParserBuilder<any>>> = {

packages/nuqs/src/serializer.test.ts

+14
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,20 @@ describe('serializer', () => {
8686
const result = serialize('?str=foo&external=kept', null)
8787
expect(result).toBe('?external=kept')
8888
})
89+
test('clears value when setting null for search param that has a default value', () => {
90+
const serialize = createSerializer({
91+
int: parseAsInteger.withDefault(0)
92+
})
93+
const result = serialize('?int=1&str=foo', { int: null })
94+
expect(result).toBe('?str=foo')
95+
})
96+
test('clears value when setting null for search param that is set to its default value', () => {
97+
const serialize = createSerializer({
98+
int: parseAsInteger.withDefault(0)
99+
})
100+
const result = serialize('?int=0&str=foo', { int: null })
101+
expect(result).toBe('?str=foo')
102+
})
89103
test('clears value when setting the default value (`clearOnDefault: true` is the default)', () => {
90104
const serialize = createSerializer({
91105
int: parseAsInteger.withDefault(0),

packages/nuqs/src/serializer.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Options } from './defs'
1+
import type { Nullable, Options } from './defs'
22
import type { inferParserType, ParserBuilder } from './parsers'
33
import { renderQueryString } from './url-encoding'
44

@@ -16,7 +16,7 @@ export function createSerializer<
1616
urlKeys?: Partial<Record<keyof Parsers, string>>
1717
} = {}
1818
) {
19-
type Values = Partial<inferParserType<Parsers>>
19+
type Values = Partial<Nullable<inferParserType<Parsers>>>
2020

2121
/**
2222
* Generate a query string for the given values.

packages/nuqs/src/tests/serializer.test-d.ts

+13
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,16 @@ import { createSerializer, parseAsInteger, parseAsString } from '../../dist'
4848
serialize({ nope: null })
4949
})
5050
}
51+
52+
// It accepts null for values
53+
{
54+
const serialize = createSerializer({
55+
foo: parseAsInteger,
56+
bar: parseAsInteger.withDefault(0)
57+
})
58+
// Should accept number | null | undefined
59+
expectType<string>(serialize({ foo: null }))
60+
expectType<string>(serialize({ foo: undefined }))
61+
expectType<string>(serialize({ bar: null }))
62+
expectType<string>(serialize({ bar: undefined }))
63+
}

0 commit comments

Comments
 (0)