query-string 9.5.1 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/base.d.ts +156 -83
  2. package/base.js +432 -246
  3. package/package.json +11 -22
  4. package/readme.md +89 -45
package/base.d.ts CHANGED
@@ -1,6 +1,7 @@
1
+ /* eslint-disable jsdoc/check-param-names -- The `pick()` and `exclude()` doc comments cover both overloads, so they document `filter` as well as `keys` and `options`. The rule only models the first overload. */
1
2
  export type ParseOptions = {
2
3
  /**
3
- Decode the keys and values. URI components are decoded with [`decode-uri-component`](https://github.com/SamVerschueren/decode-uri-component).
4
+ Decode the keys and values. A byte that is not valid UTF-8 becomes the replacement character `U+FFFD`, and the bytes around it are still decoded, like the [`URL` parser](https://developer.mozilla.org/en-US/docs/Web/API/URL) and [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) do. A `%` that is not followed by two hexadecimal digits is not an escape and is kept as it is.
4
5
 
5
6
  @default true
6
7
  */
@@ -27,6 +28,8 @@ export type ParseOptions = {
27
28
  //=> {foo: ['1', '2', '3']}
28
29
  ```
29
30
 
31
+ Entries without an index, such as `foo[]`, are appended after the highest index seen so far. Appending after an index greater than or equal to `Number.MAX_SAFE_INTEGER` is unsupported.
32
+
30
33
  - `comma`: Parse arrays with elements separated by comma:
31
34
 
32
35
  ```
@@ -34,6 +37,21 @@ export type ParseOptions = {
34
37
 
35
38
  queryString.parse('foo=1,2,3', {arrayFormat: 'comma'});
36
39
  //=> {foo: ['1', '2', '3']}
40
+
41
+ queryString.parse('foo=1,2&foo=3', {arrayFormat: 'comma'});
42
+ //=> {foo: ['1', '2', '3']}
43
+ ```
44
+
45
+ A single value has no separator, so it is a plain value, not an array with one element. Use the `types` option to always get an array, or use `bracket-separator`, which marks arrays explicitly.
46
+
47
+ ```
48
+ import queryString from 'query-string';
49
+
50
+ queryString.parse('foo=1', {arrayFormat: 'comma'});
51
+ //=> {foo: '1'}
52
+
53
+ queryString.parse('foo=1', {arrayFormat: 'comma', types: {foo: 'string[]'}});
54
+ //=> {foo: ['1']}
37
55
  ```
38
56
 
39
57
  - `separator`: Parse arrays with elements separated by a custom character:
@@ -57,16 +75,16 @@ export type ParseOptions = {
57
75
  //=> {foo: ['']}
58
76
 
59
77
  queryString.parse('foo[]=1', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
60
- //=> {foo: ['1']}
78
+ //=> {foo: ['1']}
61
79
 
62
80
  queryString.parse('foo[]=1|2|3', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
63
81
  //=> {foo: ['1', '2', '3']}
64
82
 
65
83
  queryString.parse('foo[]=1||3|||6', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
66
- //=> {foo: ['1', '', 3, '', '', '6']}
84
+ //=> {foo: ['1', '', '3', '', '', '6']}
67
85
 
68
86
  queryString.parse('foo[]=1|2|3&bar=fluffy&baz[]=4', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
69
- //=> {foo: ['1', '2', '3'], bar: 'fluffy', baz:['4']}
87
+ //=> {bar: 'fluffy', baz: ['4'], foo: ['1', '2', '3']}
70
88
  ```
71
89
 
72
90
  - `colon-list-separator`: Parse arrays with parameter names that are explicitly marked with `:list`:
@@ -88,17 +106,30 @@ export type ParseOptions = {
88
106
  ```
89
107
  */
90
108
  readonly arrayFormat?:
91
- | 'bracket'
92
- | 'index'
93
- | 'comma'
94
- | 'separator'
95
- | 'bracket-separator'
96
- | 'colon-list-separator'
97
- | 'none';
109
+ | 'bracket'
110
+ | 'index'
111
+ | 'comma'
112
+ | 'separator'
113
+ | 'bracket-separator'
114
+ | 'colon-list-separator'
115
+ | 'none';
98
116
 
99
117
  /**
100
118
  The character used to separate array elements when using `{arrayFormat: 'separator'}`.
101
119
 
120
+ Only a literal separator delimits array elements. A percent-encoded separator (for example, `%2C`) is part of the element, which is how `.stringify()` encodes elements that contain the separator. Because of this, `%` and a hexadecimal digit are unsupported, since those also appear inside percent-encoded elements, and so are `&`, `+`, and `#`, since those already have a meaning in a URL: `&` separates parameters, `+` is decoded as a space, and `#` starts the fragment identifier. Those are handled on a best-effort basis, so the result is undefined. Whitespace cannot work at all, since leading and trailing whitespace is ignored when parsing, and a lone surrogate cannot be percent-encoded, so both throw a `TypeError`.
121
+
122
+ @example
123
+ ```
124
+ import queryString from 'query-string';
125
+
126
+ queryString.parse('foo=a%2Cb,c', {arrayFormat: 'comma'});
127
+ //=> {foo: ['a,b', 'c']}
128
+
129
+ queryString.parse('foo[]=a%2Cb,c', {arrayFormat: 'bracket-separator'});
130
+ //=> {foo: ['a,b', 'c']}
131
+ ```
132
+
102
133
  @default ,
103
134
  */
104
135
  readonly arrayFormatSeparator?: string;
@@ -106,7 +137,7 @@ export type ParseOptions = {
106
137
  /**
107
138
  Supports both `Function` as a custom sorting function or `false` to disable sorting.
108
139
 
109
- If omitted, keys are sorted using `Array#sort`, which means, converting them to strings and comparing strings in Unicode code point order.
140
+ If omitted, keys are sorted using `Array#sort`.
110
141
 
111
142
  @default true
112
143
 
@@ -130,7 +161,7 @@ export type ParseOptions = {
130
161
  //=> {a: 'one', c: 'three', b: 'two'}
131
162
  ```
132
163
  */
133
- readonly sort?: ((itemLeft: string, itemRight: string) => number) | false;
164
+ readonly sort?: ((itemLeft: string, itemRight: string) => number) | boolean;
134
165
 
135
166
  /**
136
167
  Parse the value as a number type instead of string type if it's a number.
@@ -184,7 +215,7 @@ export type ParseOptions = {
184
215
 
185
216
  You can also provide a custom function to transform the value. The function will receive the raw string and should return the desired parsed result. When used with array formats (like `comma`, `separator`, `bracket`, etc.), the function is applied to each array element individually.
186
217
 
187
- NOTE: Array types (`string[]`, `number[]`) are ignored if `arrayFormat` is set to `'none'`.
218
+ NOTE: With `arrayFormat: 'none'`, array types (`string[]`, `number[]`) do not turn a single value into an array. The value stays a string, and `parseNumbers` and `parseBooleans` do not apply to it. A repeated key is still an array, and its items are parsed with the element type.
188
219
 
189
220
  @default {}
190
221
 
@@ -199,7 +230,7 @@ export type ParseOptions = {
199
230
  phoneNumber: 'string',
200
231
  }
201
232
  });
202
- //=> {phoneNumber: '+380951234567', id: 1}
233
+ //=> {id: 1, phoneNumber: '+380951234567'}
203
234
  ```
204
235
 
205
236
  @example
@@ -207,7 +238,8 @@ export type ParseOptions = {
207
238
  ```
208
239
  import queryString from 'query-string';
209
240
 
210
- queryString.parse('?age=20&items=1%2C2%2C3', {
241
+ queryString.parse('?age=20&items=1,2,3', {
242
+ arrayFormat: 'comma',
211
243
  parseNumbers: true,
212
244
  types: {
213
245
  items: 'string[]',
@@ -258,16 +290,16 @@ export type ParseOptions = {
258
290
  ```
259
291
 
260
292
  @example
261
- Array types are ignored when `arrayFormat` is set to `'none'`:
293
+ Array types do not split a single value when `arrayFormat` is set to `'none'`:
262
294
  ```
263
- queryString.parse('ids=001%2C002%2C003&foods=apple%2Corange%2Cmango', {
295
+ queryString.parse('ids=001,002,003&foods=apple,orange,mango', {
264
296
  arrayFormat: 'none',
265
297
  types: {
266
298
  ids: 'number[]',
267
299
  foods: 'string[]',
268
300
  },
269
- }
270
- //=> {ids:'001,002,003', foods:'apple,orange,mango'}
301
+ });
302
+ //=> {foods: 'apple,orange,mango', ids: '001,002,003'}
271
303
  ```
272
304
 
273
305
  @example
@@ -275,7 +307,7 @@ export type ParseOptions = {
275
307
  ```
276
308
  import queryString from 'query-string';
277
309
 
278
- queryString.parse('?ids=001%2C002%2C003&items=1%2C2%2C3&price=22%2E00&numbers=1%2C2%2C3&double=5&number=20', {
310
+ queryString.parse('?ids=001,002,003&items=1,2,3&price=22.00&numbers=1,2,3&double=5&number=20', {
279
311
  arrayFormat: 'comma',
280
312
  types: {
281
313
  ids: 'string',
@@ -286,33 +318,58 @@ export type ParseOptions = {
286
318
  number: 'number',
287
319
  },
288
320
  });
289
- //=> {ids: '001,002,003', items: ['1', '2', '3'], price: '22.00', numbers: [1, 2, 3], double: 10, number: 20}
321
+ //=> {double: 10, ids: '001,002,003', items: ['1', '2', '3'], number: 20, numbers: [1, 2, 3], price: '22.00'}
290
322
  ```
291
323
 
292
324
  @example
293
325
  Force `flagged` to be parsed as a boolean even when `parseBooleans` is false:
294
326
  ```
295
327
  queryString.parse('?isAdmin=true&flagged=true&isOkay=0', {
296
- parseBooleans: false,
297
- types: {
298
- flagged: 'boolean',
299
- isOkay: 'boolean',
300
- },
328
+ parseBooleans: false,
329
+ types: {
330
+ flagged: 'boolean',
331
+ isOkay: 'boolean',
332
+ },
301
333
  });
302
- //=> {isAdmin: 'true', flagged: true, isOkay: false}
334
+ //=> {flagged: true, isAdmin: 'true', isOkay: false}
303
335
  ```
304
336
 
305
337
  Note: The `'boolean'` type also converts `'0'` and `'1'` to booleans, and treats valueless keys (e.g. `?flag`) as `true`.
306
338
  */
307
339
  readonly types?: Record<
308
- string,
340
+ string,
309
341
  'boolean' | 'number' | 'string' | 'string[]' | 'number[]' | ((value: string) => unknown)
310
342
  >;
311
343
  };
312
344
 
313
- // eslint-disable-next-line @typescript-eslint/ban-types
345
+ // `parse('foo')` returns `{foo: null}`, so `null` is part of the result and stays in the type.
346
+ // eslint-disable-next-line @typescript-eslint/no-restricted-types
314
347
  export type ParsedQuery<T = string> = Record<string, T | null | Array<T | null>>;
315
348
 
349
+ type ParsedTypeValue<Type> =
350
+ Type extends 'number' | 'number[]' ? number
351
+ : Type extends 'boolean' ? boolean
352
+ : Type extends (value: string) => infer Result ? Result
353
+ : never;
354
+
355
+ /**
356
+ The type of a parsed value, from the `parseNumbers`, `parseBooleans`, and `types` options. A value that does not match its type stays a string.
357
+
358
+ When `types` is a non-literal value, like a variable typed as `Record<string, ...>`, the value type widens to `unknown` because the specific keys are not known. Pass an object literal to get precise types.
359
+ */
360
+ type ParsedValue<Options extends ParseOptions> =
361
+ | string
362
+ | (Options extends {parseNumbers: true} ? number : never)
363
+ | (Options extends {parseBooleans: true} ? boolean : never)
364
+ | (Options extends {types: infer Types} ? ParsedTypeValue<Types[keyof Types]> : never);
365
+
366
+ /**
367
+ Rejects a property that is not a parse or stringify option. A generic parameter is not checked for excess properties, so without this, a typo like `sortt` in an object literal would compile. Stringify options are allowed, since one options object is often shared by `parse()` and `stringify()`.
368
+
369
+ It is a mapped type instead of an intersection, so a generic wrapper that forwards its options still compiles. The wrapper gets the plain `string` value type, since its options are not known.
370
+ */
371
+ type KnownOptions<Options> = {[Key in keyof Options]: Key extends keyof ParseOptions | keyof StringifyOptions ? Options[Key] : never};
372
+
316
373
  /**
317
374
  Parse a query string into an object. Leading `?` or `#` are ignored, so you can pass `location.search` or `location.hash` directly.
318
375
 
@@ -320,14 +377,11 @@ The returned object is created with [`Object.create(null)`](https://developer.mo
320
377
 
321
378
  @param query - The query string to parse.
322
379
  */
323
- export function parse(query: string, options: {parseBooleans: true; parseNumbers: true} & ParseOptions): ParsedQuery<string | boolean | number>;
324
- export function parse(query: string, options: {parseBooleans: true} & ParseOptions): ParsedQuery<string | boolean>;
325
- export function parse(query: string, options: {parseNumbers: true} & ParseOptions): ParsedQuery<string | number>;
326
- export function parse(query: string, options?: ParseOptions): ParsedQuery;
380
+ export function parse<Options extends ParseOptions>(query: string, options?: KnownOptions<Options>): ParsedQuery<ParsedValue<Options>>;
327
381
 
328
- export type ParsedUrl = {
382
+ export type ParsedUrl<T = string> = {
329
383
  readonly url: string;
330
- readonly query: ParsedQuery;
384
+ readonly query: ParsedQuery<T>;
331
385
 
332
386
  /**
333
387
  The fragment identifier of the URL.
@@ -338,7 +392,7 @@ export type ParsedUrl = {
338
392
  };
339
393
 
340
394
  /**
341
- Extract the URL and the query string as an object.
395
+ Extract the URL and the query string as an object. Leading and trailing whitespace is ignored.
342
396
 
343
397
  @param url - The URL to parse.
344
398
  @returns An object with a `url` and `query` property.
@@ -356,7 +410,7 @@ queryString.parseUrl('https://foo.bar?foo=bar#xyz', {parseFragmentIdentifier: tr
356
410
  //=> {url: 'https://foo.bar', query: {foo: 'bar'}, fragmentIdentifier: 'xyz'}
357
411
  ```
358
412
  */
359
- export function parseUrl(url: string, options?: ParseOptions): ParsedUrl;
413
+ export function parseUrl<Options extends ParseOptions>(url: string, options?: KnownOptions<Options>): ParsedUrl<ParsedValue<Options>>;
360
414
 
361
415
  export type StringifyOptions = {
362
416
  /**
@@ -369,6 +423,8 @@ export type StringifyOptions = {
369
423
  /**
370
424
  [URL encode](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) the keys and values.
371
425
 
426
+ A lone surrogate, which cannot be percent-encoded, is replaced with the replacement character U+FFFD, like `URLSearchParams` does.
427
+
372
428
  @default true
373
429
  */
374
430
  readonly encode?: boolean;
@@ -405,7 +461,7 @@ export type StringifyOptions = {
405
461
  queryString.stringify({foo: [1, null, '']}, {arrayFormat: 'comma'});
406
462
  //=> 'foo=1,,'
407
463
  // Note that typing information for null values is lost
408
- // and `.parse('foo=1,,')` would return `{foo: [1, '', '']}`.
464
+ // and `.parse('foo=1,,', {arrayFormat: 'comma'})` would return `{foo: ['1', '', '']}`.
409
465
  ```
410
466
 
411
467
  - `separator`: Serialize arrays by separating elements with character:
@@ -441,7 +497,7 @@ export type StringifyOptions = {
441
497
  //=> 'foo[]=1||3|6'
442
498
 
443
499
  queryString.stringify({foo: [1, 2, 3], bar: 'fluffy', baz: [4]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
444
- //=> 'foo[]=1|2|3&bar=fluffy&baz[]=4'
500
+ //=> 'bar=fluffy&baz[]=4&foo[]=1|2|3'
445
501
  ```
446
502
 
447
503
  - `colon-list-separator`: Serialize arrays with parameter names that are explicitly marked with `:list`:
@@ -467,6 +523,8 @@ export type StringifyOptions = {
467
523
  /**
468
524
  The character used to separate array elements when using `{arrayFormat: 'separator'}`.
469
525
 
526
+ A separator inside an element is percent-encoded, so `.parse()` can tell it apart from the separator between elements. With `comma` and `separator`, a plain value that contains the separator is percent-encoded the same way, so it does not turn into an array. Because of this, `%` and a hexadecimal digit are unsupported, since those also appear inside percent-encoded elements, and so are `&`, `+`, and `#`, since those already have a meaning in a URL: `&` separates parameters, `+` is decoded as a space, and `#` starts the fragment identifier. Those are handled on a best-effort basis, so the result is undefined. Whitespace cannot work at all, since leading and trailing whitespace is ignored when parsing, and a lone surrogate cannot be percent-encoded, so both throw a `TypeError`.
527
+
470
528
  @default ,
471
529
  */
472
530
  readonly arrayFormatSeparator?: string;
@@ -474,7 +532,7 @@ export type StringifyOptions = {
474
532
  /**
475
533
  Supports both `Function` as a custom sorting function or `false` to disable sorting.
476
534
 
477
- If omitted, keys are sorted using `Array#sort`, which means, converting them to strings and comparing strings in Unicode code point order.
535
+ If omitted, keys are sorted using `Array#sort`.
478
536
 
479
537
  @default true
480
538
 
@@ -498,7 +556,7 @@ export type StringifyOptions = {
498
556
  //=> 'b=1&c=2&a=3'
499
557
  ```
500
558
  */
501
- readonly sort?: ((itemLeft: string, itemRight: string) => number) | false;
559
+ readonly sort?: ((itemLeft: string, itemRight: string) => number) | boolean;
502
560
 
503
561
  /**
504
562
  Skip keys with `null` as the value.
@@ -556,7 +614,7 @@ export type StringifyOptions = {
556
614
 
557
615
  Similar to the `replacer` parameter of `JSON.stringify()`, this function is called for each key-value pair and can be used to transform values before they are stringified.
558
616
 
559
- The function receives the key and value, and should return the transformed value. Returning `undefined` will omit the key-value pair from the resulting query string.
617
+ The function receives the key and value, and should return the transformed value. Returning `undefined` will omit the key-value pair from the resulting query string. The `skipNull` and `skipEmptyString` options apply to the returned value.
560
618
 
561
619
  This is useful for custom serialization of non-primitive types like `Date`:
562
620
 
@@ -602,13 +660,15 @@ Supported value types for query string parameters.
602
660
 
603
661
  Note: `Symbol`, functions, and objects (except arrays) are not supported and will throw an error when stringified.
604
662
  */
605
- export type Stringifiable = string | boolean | number | bigint | null | undefined; // eslint-disable-line @typescript-eslint/ban-types
663
+ // `null` is a value `stringify()` accepts and `parse()` can produce, so it stays in the type.
664
+ // eslint-disable-next-line @typescript-eslint/no-restricted-types
665
+ export type Stringifiable = string | boolean | number | bigint | null | undefined;
606
666
 
607
667
  /**
608
668
  A record of stringifiable values.
609
669
  */
610
670
  export type StringifiableRecord = Record<
611
- string,
671
+ string,
612
672
  Stringifiable | readonly Stringifiable[]
613
673
  >;
614
674
 
@@ -632,7 +692,7 @@ export function stringify(
632
692
  // Context: https://github.com/sindresorhus/query-string/issues/298
633
693
  // object: StringifiableRecord,
634
694
  object: Record<string, any>,
635
- options?: StringifyOptions
695
+ options?: StringifyOptions,
636
696
  ): string;
637
697
 
638
698
  /**
@@ -649,15 +709,38 @@ queryString.extract('https://foo.bar?foo=bar');
649
709
  export function extract(url: string): string;
650
710
 
651
711
  export type UrlObject = {
712
+ /**
713
+ The URL to stringify. Leading and trailing whitespace is ignored.
714
+ */
652
715
  readonly url: string;
653
716
 
654
717
  /**
655
718
  Overrides queries in the `url` property.
656
719
  */
657
- readonly query?: StringifiableRecord;
720
+ // TODO: Use the below instead when the following TS issues are fixed:
721
+ // - https://github.com/microsoft/TypeScript/issues/15300
722
+ // - https://github.com/microsoft/TypeScript/issues/42021
723
+ // Context: https://github.com/sindresorhus/query-string/issues/298
724
+ // readonly query?: StringifiableRecord;
725
+ readonly query?: Record<string, any>;
658
726
 
659
727
  /**
660
728
  Overrides the fragment identifier in the `url` property.
729
+
730
+ The fragment identifier without the leading `#`. An empty string removes the fragment identifier from the URL.
731
+
732
+ Like the query values, it is a decoded value and is percent-encoded when `encode` is `true`, so `.parseUrl()` reads it back unchanged. `/`, `?`, and `#` are kept as-is, so hash routes like `#/users?id=1` work. When `encode` is `false`, it is written as-is.
733
+
734
+ @example
735
+ ```
736
+ import queryString from 'query-string';
737
+
738
+ queryString.stringifyUrl({url: 'https://foo.bar', fragmentIdentifier: '100%'});
739
+ //=> 'https://foo.bar#100%25'
740
+
741
+ queryString.stringifyUrl({url: 'https://foo.bar', fragmentIdentifier: '/users?id=1'});
742
+ //=> 'https://foo.bar#/users?id=1'
743
+ ```
661
744
  */
662
745
  readonly fragmentIdentifier?: string;
663
746
  };
@@ -669,7 +752,9 @@ The `options` are the same as for `.stringify()`.
669
752
 
670
753
  @returns A string with the URL and a query string.
671
754
 
672
- Query items in the `query` property overrides queries in the `url` property.
755
+ Query items in the `query` property overrides queries in the `url` property. The query in the `url` property is written back from its plain string values, so `parseNumbers`, `parseBooleans`, and `types` do not rewrite it when the options are shared with `.parseUrl()`.
756
+
757
+ With `encode: false`, an array marker in an existing parameter name is only treated as a marker when it is literal, such as `a[]`, so a percent-encoded marker such as `a%5B%5D` is not overridden by the plain key. The existing parameter is kept as-is, so overriding `a` alongside an existing `a%5B%5D` in the URL writes both `a=2&a%5B%5D=1`.
673
758
 
674
759
  The `fragmentIdentifier` property overrides the fragment identifier in the `url` property.
675
760
 
@@ -693,17 +778,19 @@ queryString.stringifyUrl({
693
778
  */
694
779
  export function stringifyUrl(
695
780
  object: UrlObject,
696
- options?: StringifyOptions
781
+ options?: StringifyOptions,
697
782
  ): string;
698
783
 
699
784
  /**
700
785
  Pick query parameters from a URL.
701
786
 
702
- @param url - The URL containing the query parameters to pick.
787
+ @param url - The URL containing the query parameters to pick. Leading and trailing whitespace is ignored.
703
788
  @param keys - The names of the query parameters to keep. All other query parameters will be removed from the URL.
704
- @param filter - A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.
789
+ @param filter - A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers`, `parseBooleans`, and `types` options also affect `value`. They only affect the value passed to the filter. The query parameters are written back to the URL from the plain string values, so a value like `007` is not rewritten as `7`. The query is decoded only when `encode` is `true`, so a value like `a%20b` is not percent-encoded a second time. With `encode: false`, the filter receives the key and value as they are in the URL. The `decode` option is ignored. The stringify options apply to the output, so `skipNull` and `skipEmptyString` also drop a parameter that the filter kept.
705
790
 
706
- @returns The URL with the picked query parameters.
791
+ @param options - The parse and stringify options, which also apply to the filter. `sort: false` keeps the original parameter order in the output.
792
+
793
+ @returns The URL with the picked query parameters. The query parameters are sorted, like with `.stringify()`. Pass `sort: false` in the options to keep their order. With sorting disabled, integer-index keys come first in ascending numeric order, following JavaScript object enumeration. Repeated keys are grouped together.
707
794
 
708
795
  @example
709
796
  ```
@@ -717,32 +804,25 @@ queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value ===
717
804
  export function pick(
718
805
  url: string,
719
806
  keys: readonly string[],
720
- options?: ParseOptions & StringifyOptions
807
+ options?: ParseOptions & StringifyOptions,
721
808
  ): string;
722
- export function pick(
723
- url: string,
724
- filter: (key: string, value: string | boolean | number) => boolean,
725
- options?: {parseBooleans: true; parseNumbers: true} & ParseOptions & StringifyOptions
726
- ): string;
727
- export function pick(
809
+ export function pick<Options extends ParseOptions & StringifyOptions>(
728
810
  url: string,
729
- filter: (key: string, value: string | boolean) => boolean,
730
- options?: {parseBooleans: true} & ParseOptions & StringifyOptions
731
- ): string;
732
- export function pick(
733
- url: string,
734
- filter: (key: string, value: string | number) => boolean,
735
- options?: {parseNumbers: true} & ParseOptions & StringifyOptions
811
+ // eslint-disable-next-line unicorn/consistent-boolean-name -- `filter` is a published parameter name.
812
+ filter: (key: string, value: ParsedQuery<ParsedValue<Options>>[string]) => boolean,
813
+ options?: KnownOptions<Options>,
736
814
  ): string;
737
815
 
738
816
  /**
739
817
  Exclude query parameters from a URL. Like `.pick()` but reversed.
740
818
 
741
- @param url - The URL containing the query parameters to exclude.
819
+ @param url - The URL containing the query parameters to exclude. Leading and trailing whitespace is ignored.
742
820
  @param keys - The names of the query parameters to remove. All other query parameters will remain in the URL.
743
- @param filter - A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.
821
+ @param filter - A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers`, `parseBooleans`, and `types` options also affect `value`. They only affect the value passed to the filter. The query parameters are written back to the URL from the plain string values, so a value like `007` is not rewritten as `7`. The query is decoded only when `encode` is `true`, so a value like `a%20b` is not percent-encoded a second time. With `encode: false`, the filter receives the key and value as they are in the URL. The `decode` option is ignored. The stringify options apply to the output, so `skipNull` and `skipEmptyString` also drop a parameter that the filter kept.
822
+
823
+ @param options - The parse and stringify options, which also apply to the filter. `sort: false` keeps the original parameter order in the output.
744
824
 
745
- @returns The URL without the excluded the query parameters.
825
+ @returns The URL without the excluded query parameters. The query parameters are sorted, like with `.stringify()`. Pass `sort: false` in the options to keep their order. With sorting disabled, integer-index keys come first in ascending numeric order, following JavaScript object enumeration. Repeated keys are grouped together.
746
826
 
747
827
  @example
748
828
  ```
@@ -756,20 +836,13 @@ queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value
756
836
  export function exclude(
757
837
  url: string,
758
838
  keys: readonly string[],
759
- options?: ParseOptions & StringifyOptions
839
+ options?: ParseOptions & StringifyOptions,
760
840
  ): string;
761
- export function exclude(
762
- url: string,
763
- filter: (key: string, value: string | boolean | number) => boolean,
764
- options?: {parseBooleans: true; parseNumbers: true} & ParseOptions & StringifyOptions
765
- ): string;
766
- export function exclude(
841
+ export function exclude<Options extends ParseOptions & StringifyOptions>(
767
842
  url: string,
768
- filter: (key: string, value: string | boolean) => boolean,
769
- options?: {parseBooleans: true} & ParseOptions & StringifyOptions
770
- ): string;
771
- export function exclude(
772
- url: string,
773
- filter: (key: string, value: string | number) => boolean,
774
- options?: {parseNumbers: true} & ParseOptions & StringifyOptions
843
+ // eslint-disable-next-line unicorn/consistent-boolean-name -- `filter` is a published parameter name.
844
+ filter: (key: string, value: ParsedQuery<ParsedValue<Options>>[string]) => boolean,
845
+ options?: KnownOptions<Options>,
775
846
  ): string;
847
+
848
+ export {}; // eslint-disable-line unicorn/require-module-specifiers -- It stops the implicit export of the declarations without `export`.