Home > other >  How to make conditional interface base on props in typescript?
How to make conditional interface base on props in typescript?

Time:12-30

what is the best way to implement conditional props, i having something like this, a component that would be a view or a button that base on a props, which i call here CountdownButtonI It look like this

class CountDownButton extends Component<CountdownButtonI> {
  constructor(props:CountdownButtonI) {
    super(props);
  }

  render() {
    const { t, title, onPress, time } = this.props;

    return time ? (
      <View style={{ ...Layout.flexView }}>
        <Label text={t('common.countdown.otpCountdown')} style={{ ...LabelStyle.SubBodyText, color: Colors.black }} />
        <Label
          text={moment.utc(time * 1000).format('HH:mm:ss')}
          style={{ ...LabelStyle.SubBodyTextMedium, fontWeight: '500', color: Colors.black }}
        />
      </View>
    ) : (
      <TouchableOpacity activeOpacity={1} style={styles.buttonContainer} onPress={onPress}>
        <Label text={title} style={{ ...LabelStyle.BodyTextMedium, color: Colors.white }} />
      </TouchableOpacity>
    );
  }
}

For CountdownButtonI i set like this

interface CountdownButtonI{
  t: any;
  title: any;
  time: any;
}

But i feel it not optimal at all, like, when i have time props, onPress still visible, how can i avoid this, like, if i have time props, the onPress will raise an error

CodePudding user response:

Consider using discriminated unions.

import React from 'react'

interface WithTime {
  withTime: true
  t: (val: string) => string;
  time: number;
}

interface WithTitle {
  withTime: false
  title: string;
  onPress: () => void
}


type Props = WithTime | WithTitle


const render = (props: Props) => {
  const { withTime } = props;

  if (withTime) {
    const { t, time } = props;
    return (
      <div>
        <p>{time}</p>
        <p>{t('hello')}</p>
      </div>
    )
  }

  const { title, onPress } = props;

  return (
    <div>
      <p>{title}</p>
      <button onClick={onPress}>click</button>
    </div>
  )

}

Playground

withTime property is a discriminator. It exists in each union and has different values.

CodePudding user response:

@captain-yossarian has given you a good answer to the question that you are asking, but IMO you are asking the wrong questions.

There is no reason that your two situations (the view and the button) should be the same component. They do not share any props, any logic, any UI, or even any styles.

You are adding a level of complexity that simply does not need to be there.

Do yourself a favor and make this into two separate components. Each component will only accept the props which make sense for that specific case.

interface CountDownButtonProps {
  onPress: () => void; // or (event: GestureResponderEvent) => void
  title: string;
}

export const CountDownButton = ({ onPress, title }: CountDownButtonProps) => {
  return (
    <TouchableOpacity activeOpacity={1} style={styles.buttonContainer} onPress={onPress}>
      <Label text={title} style={{ ...LabelStyle.BodyTextMedium, color: Colors.white }} />
    </TouchableOpacity>
  );
}
interface CountDownTimerProps {
  time: number;
}

export const CountDownTimer = ({ time }: CountDownTimerProps) => {
  const { t } = useTranslation();

  return (
    <View style={{ ...Layout.flexView }}>
      <Label
        text={t('common.countdown.otpCountdown')}
        style={{ ...LabelStyle.SubBodyText, color: Colors.black }}
      />
      <Label
        text={moment.utc(time * 1000).format('HH:mm:ss')}
        style={{ ...LabelStyle.SubBodyTextMedium, fontWeight: '500', color: Colors.black }}
      />
    </View>
  );
}
  • Related