pbeshai / use-query-params

React Hook for managing state in URL query parameters with easy serialization.

Home Page:https://pbeshai.github.io/use-query-params

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Can StringParam be restricted in TS to a subset of strings without type casting?

sevgit opened this issue · comments

Hey there, I was wondering if there was a way to use StringParam but restricting it to a specific set of strings.

For example, say I have a type for FieldKeys which is a union of strings derived from the fields on an object. Is there any type-safe way to use StringParam to achieve something like QueryParamConfig<FieldKey | null | undefined>?

Currently I tried something like this:

type QueryConfig<FieldKey extends string> = {
  sortKey: QueryParamConfig<FieldKey | null | undefined>;
};

...

const [query, setQuery] = useQueryParams<QueryConfig<FieldKey>>({
    sortKey: StringParam
  });

But I'm getting errors StringParam's type is obviously wider.

Any ideas? Should we use something other than StringParam? I've looked at enum params but we're not using enums, just inferring fields from other TS types.

Are you sure createEnumParam doesn't work for you? It sounds like what you're looking for. It uses strings, not enums and is described in the serialize-query-params README:

Enum Param

You can define enum param using createEnumParam. It works as StringParam but restricts decoded output to a list of allowed strings:

import { createEnumParam } from 'serialize-query-params';

// values other than 'asc' or 'desc' will be decoded as undefined
const SortOrderEnumParam = createEnumParam(['asc', 'desc'])

But more directly for your use-case based on types only (it would not validate like createEnumParam does), you can do something like any of these optinos:

const MyFieldParam = StringParam as QueryParamConfig<'abc' | 'def' | null | undefined>

const [x] = useQueryParam('x', MyFieldParam)

const [y] = useQueryParam('y', StringParam as QueryParamConfig<'abc' | 'def' | null | undefined>)

function makeFieldParam<Values extends string>() {
  return StringParam as QueryParamConfig<Values | null | undefined>
}

const [z] = useQueryParam('z', makeFieldParam<'abc'|'def'>())


// this one has the same type but actually validates with js too
const MyParam = createEnumParam(['abc', 'def'])
const [a] = useQueryParam('a', MyParam)

In all of these cases the value has type 'abc' | 'def' | null | undefined

This helps a lot, thanks for the thorough example.

Closing this.