skip to Main Content

In react I’m trying to set a conditional type in a function argument depending if a prop is pass in the component.

Here is the example, I have this component:

const DatePicker = ({
  handleDateChange,
  value,
  objectKey,
}: {
  handleDateChange: (value: Dayjs | { [x: string]: Dayjs }) => void;
  value: Dayjs;
  objectKey?: string;
}) => JSX

What I want to do is that, if "objectKey" is pass as props than the type of the paremeter "value" in the function handleDateChange would be { [x: string]: Dayjs }, otherwise it would be value: Dayjs.

Does anyone have an idea of how to make this?

2

Answers


  1. You can achieve something like this with function overloads.

    // first overload signature
    function DatePicker({
      handleDateChange,
      value,
    }: {
      handleDateChange: (value: Dayjs) => void;
      value: Dayjs;
    }): JSX;
    // second overload signature
    function DatePicker({
      handleDateChange,
      value,
      objectKey,
    }: {
      handleDateChange: (value: { [x: string]: Dayjs }) => void;
      value: Dayjs;
      objectKey?: string;
    }): JSX;
    // implementation
    function DatePicker({
      handleDateChange,
      value,
      objectKey,
    }: {
      handleDateChange: ((value: Dayjs) => void) | ((value: { [x: string]: Dayjs }) => void);
      value: Dayjs;
      objectKey?: string;
    }) {
      // ...
    }
    

    With this approach DatePicker can only be called according to your definition, however the implementation won’t know the correlation between handleDateChange and objectKey so you still need to handle that explicitly.

    Without knowing any details of DatePicker I think it would be better to only use one signature and let the parent component make changes to fit DatePicker instead of having DatePicker handle special cases from it’s parents.

    Login or Signup to reply.
  2. The following type should do what you are interested in, using Unions and Intersections:

    type DatePickerProps = {
        value: Dayjs;  
    } & ({
        objectKey?: undefined;
        handleDateChange: (value: Dayjs) => void;
    } | {
        objectKey: string;
        handleDateChange: (value: { [x: string]: Dayjs }) => void;
    });
    

    Explanation:

    • Since value is always part of the type, we add it first to get it out of the way, and so that we don’t duplicate it.
    • Then we intersect (&) that part with the union (|) of the two situations that can vary according to your rules, specifically:
    • The signature of handleDateChange varies based on whether the type of objectKey is string or undefined (not set).
    • We define objectKey differently (first with ?, then without) because we want to let TypeScript know it is completely omittable when not set.

    You can simply assign it to the props parameter object:

    const DatePicker =
        ({handleDateChange, value, objectKey}: DatePickerProps) =>
            ...
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search