From 2faff699a15418c1ce66dceb40aeb8f8ef918589 Mon Sep 17 00:00:00 2001 From: Martin Cura Date: Fri, 15 Nov 2024 07:52:15 -0300 Subject: [PATCH 1/3] fix(serializer/types): allow `null` to serialize param whose parser has default --- packages/nuqs/src/parsers.ts | 19 ++++++++++++++----- packages/nuqs/src/serializer.test.ts | 14 ++++++++++++++ packages/nuqs/src/serializer.ts | 4 ++-- packages/nuqs/src/tests/serializer.test-d.ts | 13 +++++++++++++ 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/packages/nuqs/src/parsers.ts b/packages/nuqs/src/parsers.ts index 82973a601..e9c76b3a6 100644 --- a/packages/nuqs/src/parsers.ts +++ b/packages/nuqs/src/parsers.ts @@ -421,13 +421,13 @@ export function parseAsArrayOf( } type inferSingleParserType = Parser extends ParserBuilder< - infer Type + infer Value > & { - defaultValue: infer Type + defaultValue: infer Value } - ? Type - : Parser extends ParserBuilder - ? Type | null + ? Value + : Parser extends ParserBuilder + ? Value | null : never type inferParserRecordType>> = { @@ -464,3 +464,12 @@ export type inferParserType = : Input extends Record> ? inferParserRecordType : never + +type inferSingleSerializerType = + Parser extends ParserBuilder ? Value | null : never + +export type inferSerializerRecordType< + Map extends Record> +> = { + [Key in keyof Map]: inferSingleSerializerType +} diff --git a/packages/nuqs/src/serializer.test.ts b/packages/nuqs/src/serializer.test.ts index e924cc660..5ef06ae53 100644 --- a/packages/nuqs/src/serializer.test.ts +++ b/packages/nuqs/src/serializer.test.ts @@ -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), diff --git a/packages/nuqs/src/serializer.ts b/packages/nuqs/src/serializer.ts index 6b9b2a540..216eaae51 100644 --- a/packages/nuqs/src/serializer.ts +++ b/packages/nuqs/src/serializer.ts @@ -1,5 +1,5 @@ import type { Options } from './defs' -import type { inferParserType, ParserBuilder } from './parsers' +import type { inferSerializerRecordType, ParserBuilder } from './parsers' import { renderQueryString } from './url-encoding' type Base = string | URLSearchParams | URL @@ -16,7 +16,7 @@ export function createSerializer< urlKeys?: Partial> } = {} ) { - type Values = Partial> + type Values = Partial> /** * Generate a query string for the given values. diff --git a/packages/nuqs/src/tests/serializer.test-d.ts b/packages/nuqs/src/tests/serializer.test-d.ts index 55a5eaf73..f07eb5215 100644 --- a/packages/nuqs/src/tests/serializer.test-d.ts +++ b/packages/nuqs/src/tests/serializer.test-d.ts @@ -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(serialize({ foo: null })) + expectType(serialize({ foo: undefined })) + expectType(serialize({ bar: null })) + expectType(serialize({ bar: undefined })) +} From 4d52b64fd039a25a5907aee6d38cfb14cdecf531 Mon Sep 17 00:00:00 2001 From: Martin Cura Date: Fri, 15 Nov 2024 08:06:02 -0300 Subject: [PATCH 2/3] ref: move serializer code to serializer file, duh --- packages/nuqs/src/parsers.ts | 9 --------- packages/nuqs/src/serializer.ts | 11 ++++++++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/nuqs/src/parsers.ts b/packages/nuqs/src/parsers.ts index e9c76b3a6..9a94681b2 100644 --- a/packages/nuqs/src/parsers.ts +++ b/packages/nuqs/src/parsers.ts @@ -464,12 +464,3 @@ export type inferParserType = : Input extends Record> ? inferParserRecordType : never - -type inferSingleSerializerType = - Parser extends ParserBuilder ? Value | null : never - -export type inferSerializerRecordType< - Map extends Record> -> = { - [Key in keyof Map]: inferSingleSerializerType -} diff --git a/packages/nuqs/src/serializer.ts b/packages/nuqs/src/serializer.ts index 216eaae51..ab66927ac 100644 --- a/packages/nuqs/src/serializer.ts +++ b/packages/nuqs/src/serializer.ts @@ -1,10 +1,19 @@ import type { Options } from './defs' -import type { inferSerializerRecordType, ParserBuilder } from './parsers' +import type { ParserBuilder } from './parsers' import { renderQueryString } from './url-encoding' type Base = string | URLSearchParams | URL type ParserWithOptionalDefault = ParserBuilder & { defaultValue?: T } +type inferSingleSerializerType = + Parser extends ParserBuilder ? Value | null : never + +export type inferSerializerRecordType< + Map extends Record> +> = { + [Key in keyof Map]: inferSingleSerializerType +} + export function createSerializer< Parsers extends Record> >( From 877d1d379adb71101a485e72aaab4548a14ebcf6 Mon Sep 17 00:00:00 2001 From: Martin Cura Date: Fri, 15 Nov 2024 09:40:19 -0300 Subject: [PATCH 3/3] ref: reuse `inferParserType`, rm unnecessary serializer variants --- packages/nuqs/src/serializer.ts | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/nuqs/src/serializer.ts b/packages/nuqs/src/serializer.ts index ab66927ac..272c05b18 100644 --- a/packages/nuqs/src/serializer.ts +++ b/packages/nuqs/src/serializer.ts @@ -1,19 +1,10 @@ -import type { Options } from './defs' -import type { ParserBuilder } from './parsers' +import type { Nullable, Options } from './defs' +import type { inferParserType, ParserBuilder } from './parsers' import { renderQueryString } from './url-encoding' type Base = string | URLSearchParams | URL type ParserWithOptionalDefault = ParserBuilder & { defaultValue?: T } -type inferSingleSerializerType = - Parser extends ParserBuilder ? Value | null : never - -export type inferSerializerRecordType< - Map extends Record> -> = { - [Key in keyof Map]: inferSingleSerializerType -} - export function createSerializer< Parsers extends Record> >( @@ -25,7 +16,7 @@ export function createSerializer< urlKeys?: Partial> } = {} ) { - type Values = Partial> + type Values = Partial>> /** * Generate a query string for the given values.