Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add literal parsers #453

Merged
merged 4 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ import {
parseAsArrayOf,
parseAsJson,
parseAsStringEnum,
parseAsStringConst
parseAsLiteral
} from 'nuqs'

useQueryState('tag') // defaults to string
Expand All @@ -130,12 +130,12 @@ const [direction, setDirection] = useQueryState(
.withDefault(Direction.up)
)

// readonly Array (string-based only)
// Literals (string- or number-based only)
const colors = ['red', 'green', 'blue'] as const

const [color, setColor] = useQueryState(
'color',
parseAsStringConst(colors) // pass a readonly list of allowed values
parseAsLiteral(colors) // pass a readonly list of allowed values
.withDefault('red')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Update the doc to include a valid default value, like 4: https://xkcd.com/221/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I must have overlooked that.

)
```
Expand Down
6 changes: 3 additions & 3 deletions packages/docs/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ import {
parseAsArrayOf,
parseAsJson,
parseAsStringEnum,
parseAsStringConst
parseAsLiteral
} from 'nuqs'

useQueryState('tag') // defaults to string
Expand All @@ -121,12 +121,12 @@ const [direction, setDirection] = useQueryState(
.withDefault(Direction.up)
)

// readonly Array (string-based only)
// Literals (string- or number-based only)
const colors = ['red', 'green', 'blue'] as const

const [color, setColor] = useQueryState(
'color',
parseAsStringConst(colors) // pass a readonly list of allowed values
parseAsLiteral(colors) // pass a readonly list of allowed values
.withDefault('red')
)
```
Expand Down
14 changes: 7 additions & 7 deletions packages/nuqs/src/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ export function parseAsStringEnum<Enum extends string>(validValues: Enum[]) {
}

/**
* String-based readonly lists provide better type-safety for known sets of values.
* You will need to pass the parseAsStringConst function a list of your string values
* String- or number-based readonly lists provide better type-safety for known sets of values.
* You will need to pass the parseAsLiteral function a list of your string or number values
* in order to validate the query string. Anything else will return `null`,
* or your default value if specified.
*
Expand All @@ -241,25 +241,25 @@ export function parseAsStringEnum<Enum extends string>(validValues: Enum[]) {
*
* const [color, setColor] = useQueryState(
* 'color',
* parseAsStringConst(colors) // pass a readonly list of allowed values
* parseAsLiteral(colors) // pass a readonly list of allowed values
* .withDefault("red")
* )
* ```
*
* @param validValues The values you want to accept
*/
export function parseAsStringConst<Const extends string>(
validValues: readonly Const[]
export function parseAsLiteral<Literal extends string | number>(
validValues: readonly Literal[]
) {
return createParser({
parse: (query: string) => {
const asConst = query as unknown as Const
const asConst = query as unknown as Literal
if (validValues.includes(asConst)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I meant by having to extend the existing numeric parsers: using number literals here won't work as the array [1, 2, 3] never contains the value "2", it has to be parsed first.

We could hack our way out by detecting if the first element of the validValues is a number, then pass it through parseFloat, which handles all cases for numeric literals.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be enough to provide 2 functions, that looks too much like a hack to me? parseAsStringLiteral and parseAsNumberLiteral

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a good plan indeed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take a look at this.

By the way, I can't think of any other uses for literals in query than String and Number. So a generic attempt would not really achieve much. You can always create your own parser for special use-cases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only other allowed literal type is boolean, and we already have a parser for that one, so this is perfect, thanks!

return asConst
}
return null
},
serialize: (value: Const) => value.toString()
serialize: (value: Literal) => value.toString()
})
}

Expand Down