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.