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

parseAsArrayOf for readonly T[]? #751

Open
alula opened this issue Nov 9, 2024 · 1 comment
Open

parseAsArrayOf for readonly T[]? #751

alula opened this issue Nov 9, 2024 · 1 comment
Labels
feature New feature or request parsers/built-in Related to built-in parsers parsers/community Related to community parsers

Comments

@alula
Copy link

alula commented Nov 9, 2024

Context

What's your version of nuqs?

"nuqs": "^2.1.1"

What framework are you using?

  • ✅ Next.js (app router)
  • ❌ Next.js (pages router)
  • ❌ React SPA (no router)
  • ❌ Remix
  • ❌ React Router
  • ❌ Other (please specify)

Which version of your framework are you using?

"next": "^15.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1"

Description

parseAsArrayOf specifies mutable arrays in it's types, which is slightly annoying if you want to pass readonly T[] into functions like withDefault or returned setState, as they expect you to pass a mutable array type.

I'm not sure if making it readonly would have any downsides.

Reproduction

import { useQueryState } from 'nuqs';
import { parseAsArrayOf, parseAsStringLiteral } from 'nuqs/parsers';

// ....

const TYPE_OF_PLATFORM = ['web', 'ios', 'android'] as const;
const TYPE_OF_PLATFORM_DEFAULT = ['web'] as const;

const [typeOfPlatform, setTypeOfPlatform] = useQueryState(
  "type",
  parseAsArrayOf(parseAsStringLiteral(TYPE_OF_PLATFORM)).withDefault(
    TYPE_OF_PLATFORM_DEFAULT // type error
  )
);

setTypeOfPlatform(TYPE_OF_PLATFORM_DEFAULT); // type error
@alula alula added the bug Something isn't working label Nov 9, 2024
@franky47
Copy link
Member

franky47 commented Nov 10, 2024

If you're only interested in type-level immutability (as const, readonly keywords), you could define a utility that casts the parser type:

import { type ParserBuilder } from 'nuqs/server'

function parseAsReadonly<T>(parser: ParserBuilder<T>) {
  return parser as ParserBuilder<Readonly<T>>
}

// Usage:
const TYPE_OF_PLATFORM = ['web', 'ios', 'android'] as const
const TYPE_OF_PLATFORM_DEFAULT = ['web'] as const

function usePlatformType() {
  const [typeOfPlatform, setTypeOfPlatform] = useQueryState(
    'type',
    parseAsReadonly(
      parseAsArrayOf(parseAsStringLiteral(TYPE_OF_PLATFORM))
    ).withDefault(
      TYPE_OF_PLATFORM_DEFAULT
    )
  )

  const reset = () => setTypeOfPlatform(TYPE_OF_PLATFORM_DEFAULT)
}

If you want runtime-immutability, you could make that wrapper act like an actual parser that freezes the parsed query string:

import { createParser } from 'nuqs/server'

function parseAsReadonly<T>(parser: ParserBuilder<T>) {
  return createParser<Readonly<T>>({
    ...parser,
    parse(query) {
      const value = parser.parse(query)
      if (value === null) {
        return null
      }
      return Object.freeze(value)
    },
  })
}

@franky47 franky47 added feature New feature or request parsers/built-in Related to built-in parsers parsers/community Related to community parsers and removed bug Something isn't working labels Nov 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request parsers/built-in Related to built-in parsers parsers/community Related to community parsers
Projects
None yet
Development

No branches or pull requests

2 participants