Moon
Moon

Reputation: 890

React Typescript distinguish between different prop types, choosing from one of two types

I have two Typescript interfaces:

type ISecond = {
    timeType: string
    secondTime: number
}

type IDay = {
    timeType: string
    startTime: number
    endTime: number
}

With my react function props types,

...
const CountDown: React.FC<Iprops> = (params: Iprops) => {
    ...
}
...

How can I make it only choose from one of these two? If my props has secondTime, it cannot have props startTime and endTime.

I'm using type Iprops = ISecond | IDay, but cannot limit it only choose from one.

Upvotes: 1

Views: 937

Answers (3)

There are several ways to handle it.

The simplest way

Just add one sharable property. For example: type



type ISecond = {
  type: 'second',
  timeType: string
  secondTime: number
}

type IDay = {
  type: 'day',
  timeType: string
  startTime: number
  endTime: number
}

type IProps = ISecond | IDay;


const Foo = (prop: IProps) => {
  if (prop.type === 'day') {
    const x = prop; // IDay
  }
  if (prop.type === 'second') {
    const x = prop; // ISecond
  }
}

Or you can go by a bit longer way, but I believe more safer. You can add typeguards and leave your types as is:

  type Base = {
    timeType: string
  }

  type ISecond = Base & {
    secondTime: number
  }

  type IDay = Base & {
    startTime: number
    endTime: number
  }

  type IProps = ISecond | IDay;

  const hasProperty = <T extends object>(obj: T, prop: string) =>
    Object.hasOwnProperty.call(obj, prop);

  const isSecond = (obj: IProps):obj is ISecond => hasProperty(obj, 'secondTime')


  const Foo = (prop: IProps) => {
    if (isSecond(prop)) {
      const x = prop; // ISecond
    } else {
      const x = prop; // IDay
    }
  }

Playground

More information about using union with react components you can find in my article here

Upvotes: 2

Alexandre
Alexandre

Reputation: 2050

Use function overloading

function CountDown (params: ISecond);
function CountDown (params: IDay) {
    ...
}

Check the documentation.

Upvotes: 0

yudhiesh
yudhiesh

Reputation: 6829

You need to be able to distinguish the different types of props that you are using which can be achieved using the in operator. As the IDay interface contains the property endTime and ISecond does not you can use it to differentiate the two(you can also use startTime here as well).

const CountDown: React.FC<Iprops> = (params: Iprops) => {
   'endTime' in params ? (
    // Logic for using IDay goes here 
  ) : (
    // Logic for using ISecond goes here 
  )
}

Upvotes: 0

Related Questions