Skip to content

Commit

Permalink
fix(serializer/types): Allow null value in serializer for parsers w…
Browse files Browse the repository at this point in the history
…ith 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.
  • Loading branch information
MartinCura authored Nov 15, 2024
1 parent 1c46a8e commit 84c5189
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 7 deletions.
10 changes: 5 additions & 5 deletions packages/nuqs/src/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,13 +421,13 @@ export function parseAsArrayOf<ItemType>(
}

type inferSingleParserType<Parser> = Parser extends ParserBuilder<
infer Type
infer Value
> & {
defaultValue: infer Type
defaultValue: infer Value
}
? Type
: Parser extends ParserBuilder<infer Type>
? Type | null
? Value
: Parser extends ParserBuilder<infer Value>
? Value | null
: never

type inferParserRecordType<Map extends Record<string, ParserBuilder<any>>> = {
Expand Down
14 changes: 14 additions & 0 deletions packages/nuqs/src/serializer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ describe('serializer', () => {
const result = serialize('?str=foo&external=kept', null)
expect(result).toBe('?external=kept')
})
test('clears value when setting null for search param that has a default value', () => {
const serialize = createSerializer({
int: parseAsInteger.withDefault(0)
})
const result = serialize('?int=1&str=foo', { int: null })
expect(result).toBe('?str=foo')
})
test('clears value when setting null for search param that is set to its default value', () => {
const serialize = createSerializer({
int: parseAsInteger.withDefault(0)
})
const result = serialize('?int=0&str=foo', { int: null })
expect(result).toBe('?str=foo')
})
test('clears value when setting the default value (`clearOnDefault: true` is the default)', () => {
const serialize = createSerializer({
int: parseAsInteger.withDefault(0),
Expand Down
4 changes: 2 additions & 2 deletions packages/nuqs/src/serializer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Options } from './defs'
import type { Nullable, Options } from './defs'
import type { inferParserType, ParserBuilder } from './parsers'
import { renderQueryString } from './url-encoding'

Expand All @@ -16,7 +16,7 @@ export function createSerializer<
urlKeys?: Partial<Record<keyof Parsers, string>>
} = {}
) {
type Values = Partial<inferParserType<Parsers>>
type Values = Partial<Nullable<inferParserType<Parsers>>>

/**
* Generate a query string for the given values.
Expand Down
13 changes: 13 additions & 0 deletions packages/nuqs/src/tests/serializer.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,16 @@ import { createSerializer, parseAsInteger, parseAsString } from '../../dist'
serialize({ nope: null })
})
}

// It accepts null for values
{
const serialize = createSerializer({
foo: parseAsInteger,
bar: parseAsInteger.withDefault(0)
})
// Should accept number | null | undefined
expectType<string>(serialize({ foo: null }))
expectType<string>(serialize({ foo: undefined }))
expectType<string>(serialize({ bar: null }))
expectType<string>(serialize({ bar: undefined }))
}

0 comments on commit 84c5189

Please sign in to comment.