1
1
import type { Options } from './defs'
2
2
import { safeParse } from './utils'
3
3
4
+ type Require < T , Keys extends keyof T > = Pick < Required < T > , Keys > & Omit < T , Keys >
5
+
4
6
export type Parser < T > = {
5
7
parse : ( value : string ) => T | null
6
8
serialize ?: ( value : T ) => string
9
+ eq ?: ( a : T , b : T ) => boolean
7
10
}
8
11
9
12
export type ParserBuilder < T > = Required < Parser < T > > &
@@ -70,7 +73,9 @@ export type ParserBuilder<T> = Required<Parser<T>> &
70
73
* Wrap a set of parse/serialize functions into a builder pattern parser
71
74
* you can pass to one of the hooks, making its default value type safe.
72
75
*/
73
- export function createParser < T > ( parser : Required < Parser < T > > ) : ParserBuilder < T > {
76
+ export function createParser < T > (
77
+ parser : Require < Parser < T > , 'parse' | 'serialize' >
78
+ ) : ParserBuilder < T > {
74
79
function parseServerSideNullable ( value : string | string [ ] | undefined ) {
75
80
if ( typeof value === 'undefined' ) {
76
81
return null
@@ -91,6 +96,7 @@ export function createParser<T>(parser: Required<Parser<T>>): ParserBuilder<T> {
91
96
}
92
97
93
98
return {
99
+ eq : ( a , b ) => a === b ,
94
100
...parser ,
95
101
parseServerSide : parseServerSideNullable ,
96
102
withDefault ( defaultValue ) {
@@ -318,7 +324,11 @@ export function parseAsJson<T>(parser?: (value: unknown) => T) {
318
324
return null
319
325
}
320
326
} ,
321
- serialize : value => JSON . stringify ( value )
327
+ serialize : value => JSON . stringify ( value ) ,
328
+ eq ( a , b ) {
329
+ // Check for referential equality first
330
+ return a === b || JSON . stringify ( a ) === JSON . stringify ( b )
331
+ }
322
332
} )
323
333
}
324
334
@@ -333,6 +343,7 @@ export function parseAsArrayOf<ItemType>(
333
343
itemParser : Parser < ItemType > ,
334
344
separator = ','
335
345
) {
346
+ const itemEq = itemParser . eq ?? ( ( a : ItemType , b : ItemType ) => a === b )
336
347
const encodedSeparator = encodeURIComponent ( separator )
337
348
// todo: Handle default item values and make return type non-nullable
338
349
return createParser ( {
@@ -361,6 +372,15 @@ export function parseAsArrayOf<ItemType>(
361
372
: String ( value )
362
373
return str . replaceAll ( separator , encodedSeparator )
363
374
} )
364
- . join ( separator )
375
+ . join ( separator ) ,
376
+ eq ( a , b ) {
377
+ if ( a === b ) {
378
+ return true // Referentially stable
379
+ }
380
+ if ( a . length !== b . length ) {
381
+ return false
382
+ }
383
+ return a . every ( ( value , index ) => itemEq ( value , b [ index ] ! ) )
384
+ }
365
385
} )
366
386
}
0 commit comments