TypeScript setQuery typing for custom parameter types
glensomerville opened this issue · comments
I have a custom parameter defined as:
const sortableColumns = [
'name',
'status',
'createdDate',
] as const;
type SortBy = typeof sortableColumns[number];
const SortParam = {
encode: (sortBy: string): SortBy =>
(sortableColumns as ReadonlyArray<string>).includes(sortBy)
? (sortBy as unknown as SortBy)
: 'createdDate',
decode: (sortBy: string | (string | null)[] | null | undefined): SortBy =>
(
sortableColumns as ReadonlyArray<string | (string | null)[] | null | undefined>
).includes(sortBy)
? (sortBy as unknown as SortBy)
: 'createdDate',
When attempting to call setQuery
from the useQueryParams
hook with the following sorting function:
const [{ sort }, setQuery] = useQueryParams({
sort: withDefault(SortParam, 'name', false),
});
const onSortChange = (sort: string) => {
setQuery({ sort }); // Type error
};
I get the error Type 'string' is not assignable to type '"name" | "status" | "createdDate"'
.
This seems to be due to the setQuery
function expecting a value of type DecodedValueMap
which expects the type of sort
to be SortBy
. However, I would expect it to accept the type EncodedValueMap
when setting the query value allowing for sort
to be of type string
as expected when passed to the encode
function of the custom parameter map.
Is this a bug, or am I perhaps using it wrong?
The expectation is you are working with values in their normal usage form and the library handles the encoding/decoding for you. So you basically always work with a decoded value in your code (outside of creating a custom param). e.g. for an ObjectParam, you'd do:
setQuery({ obj: { foo: 'bar' } })
not
setQuery({ obj: 'foo-bar' })
The same principle applies here.
Also, for your particular use case, you may find createEnumParam
handy for generating your SortParam.
Thanks for the clarification. This helps 👍
Using the createEnumParam
seems to do the job without so much customisation as you said. However, when I attempt to use it together with withDefault
to avoid nulls, the type still contains null | undefined
.
e.g. if I use the above example with createEnumParam
and withDefault
:
sortBy: withDefault(createEnumParam<SortBy>([... sortableColumns]), 'createdDate', false),
the type of sortBy
remains 'name' | 'status' | 'createdDate' | null | undefined
. I can work around it however.
Perhaps there's a bug with the createEnumParam
and withDefault
combination though?
Ah yes this is a weird thing I have never figured out how to get the types to work effortlessly with. You need to use as const
after your default value:
sortBy: withDefault(createEnumParam<SortBy>([... sortableColumns]), 'createdDate' as const, false),
This will make sortBy
have type 'name' | 'status' | 'createdDate' | null
(you passed false
meaning you want nulls, default is nulls are excluded with withDefault)
Awesome, this solved it! Thanks 👍