I have these filtering/pagination variables:
export interface FilterVariables {
chartId?: string | null;
screenId?: string | null;
first?: number | null;
...
}
I have a number of handlers I need to create for controls to mutate these variables:
const handleSetChartId = (chartId: string) => {
setFilters((current) => ({
...current,
chartId,
}));
};
const handleSetFirst = (first: number) => {
setFilters((current) => ({
...current,
first,
}));
};
It would be convenient to partially apply the filter type and create a more generic function that would generate my handlers:
const handleSetFilter = (filter: keyof FilterVariables) => (
value: FilterVariables[typeof filter],
) => {
setFilters((current) => ({
...current,
filter: value,
}));
};
This works, but value
is typed to string | number | null | undefined
- is there any way to better constrain value
?
CodePudding user response:
With a combination of generics and NonNullable utility type, you can write:
const handleSetFilter = <K extends keyof FilterVariables>(filter: K) =>
(value: NonNullable<FilterVariables[K]>) => {
setFilters((current) => ({
...current,
filter: value,
}));
};
const setChartId = handleSetFilter('chartId')
// inferred to const setChartId: (value: string) => void
Alternatively, you can create only one function taking an object with any subset of filters, instead of generating separate handlers:
const handleFilterUpdate = (f: Partial<FilterVariables>) => {
setFilters((current) => ({
...current,
...f,
}));
}
handleFilterUpdate({chartId: '1212'})
CodePudding user response:
We can make use of a generic to "store" which key was used in the call, then use it in the returned function to get the correct type:
function handleSetFilter<
K extends keyof FilterVariables
>(filter: K): (value: FilterVariables[K]) => unknown { ... }