LukaszBielsko
LukaszBielsko

Reputation: 321

How to type a type paramater in react Typescript?

I'm trying to create a generic hook that returns a function that clears the filter values.

I'm not passing any parameters, just a type parameter. FilterParams will be in the shape of example SomeFilterParams.

//... 

export const useBuildClearValue = <FilterParams>() => {
  const {
    setFieldValue,
  } = useFormikContext();

  const buildClearValue = useCallback((inputName: keyof FilterParams) => {
    return () => {
      setFieldValue(inputName, null); //error is here
    };
  }, [
    setFieldValue,
  ]);

  return buildClearValue;

};

//... USAGE

type SomeFilterParams = {
  name?: string;
  status?: string;
}

const buildClearValue = useBuildClearValue<SomeFilterParams>();

TS is not happy with inputName in the setFieldValue.

Error: Argument of type 'string | number | symbol' is not assignable to parameter of type 'string'. Type 'number' is not assignable to type 'string'.ts(2345)

I guess that I need to type the passed type itself and enforce the key as a string and value as a string as well.

Any idea how this can be achieved?

Upvotes: 0

Views: 50

Answers (1)

Jimmy
Jimmy

Reputation: 37081

You didn't show what the signature of setFieldValue is, but based on the error and your example usage, it looks like it takes a property name as a string and a value to set for that property. However, keyof T without any constraints on T is defined as any of the types that are valid for keys in JavaScript, which is strings, numbers, and symbols. Because you can't pass a number or symbol to setFieldValue, you get this error.

What you want is to restrict the input to only the string property names of your generic type. You can do that by filtering all number and symbol keys out using a conditional mapped type.

Here's a simplified example, removing the React and Formik-specific parts.

declare const setFieldValue: (property: string, value: unknown) => void;

export const useBuildClearValue = <T,>() => {
  return (inputName: Exclude<keyof T, number | symbol>) => {
    return () => {
      setFieldValue(inputName, null);
    };
  };
};

type SomeFilterParams = {
  name?: string;
  status?: string;
}

const buildClearValue = useBuildClearValue<SomeFilterParams>();
buildClearValue("name");
buildClearValue("status");

TS Playground

Upvotes: 1

Related Questions