Home > OS >  How can I improve the typing of this partially applied function?
How can I improve the typing of this partially applied function?

Time:03-23

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 { ... }

Playground

  • Related