Edupala

Comprehensive Full Stack Development Tutorial: Learn Ionic, Angular, React, React Native, and Node.js with JavaScript

Mastering Conditional Props with Discriminated Unions in React TypeScript

Discriminated Unions in TypeScript provide a powerful mechanism for handling various types in a concise and type-safe manner. In the context of React, Discriminated Unions can be particularly useful when dealing with components that render different content based on the type of data they receive.


Discriminated unions shine when you need type-safe handling of different possibilities with distinct properties. Discriminated Unions in TypeScript are useful when you want to create complex type structures by combining two or more types into a single type based on a common property or key. It is a way of combining two or more types into a single type, based on a common property or key.

Example of Discriminated Unions in React TypeScript

Here are a few examples of how you can use Discriminated Unions in React with TypeScript with useState with discriminated union typescript.

import React, { useState, useEffect } from 'react';

// Discriminated union for user data or error
type UserResponse = {
  type: 'success';
  user: {
    name: string;
    email: string;
  };
} | {
  type: 'error';
  message: string;
};

function UserDetails() {
  const [response, setResponse] = useState<UserResponse | null>(null);

  useEffect(() => {
    // Simulate fetching user data or handling an error
    const fetchData = async () => {
      try {
        const userData = await fetchUserAPI(); // Replace with actual API call
        setResponse({ type: 'success', user: userData });
      } catch (error) {
        setResponse({ type: 'error', message: error.message });
      }
    };

    fetchData();
  }, []);

  return (
    <div>
      {response ? (
        <div>
          {response.type === 'success' ? (
            <>
              <h2>User Details</h2>
              <p>Name: {response.user.name}</p>
              <p>Email: {response.user.email}</p>
            </>
          ) : (
            <p>Error: {response.message}</p>
          )}
        </div>
      ) : (
        <p>Loading user data...</p>
      )}
    </div>
  );
}

In this example, we have two types representing a successful and failed API response. We combine them into a single type Response using the pipe operator (|). TypeScript ensures that properties like user.name and message are only accessed when their respective types are guaranteed, preventing potential errors. When type is success then we can access user object with its value name and email.

Note: We combine different types into a single type using the pipe operator (`|`)

Different examples of discriminated union

Here are some glimse of other discrcimianted union types

type SuccessMessage = {
  type: 'success';
  content: string;
};

type ErrorMessage = {
  type: 'error';
  content: string;
};

type WarningMessage = {
  type: 'warning';
  content: string;
  ... can have addition
};

type Message = SuccessMessage | ErrorMessage | WarningMessage;

Issues in Infobox we conditional require one of the two modes, we need severity attribute only when we have modeset to warning
Without using severity propreties we will get error. To fixed it we have two solution

It is a way of combining two or more types into a single type, based on a common property or key. Here are a few examples of how you can use Discriminated Unions in React with TypeScript:

Uses case of Discriminated union in funtion argument

Lets first used it in function

interface Success {
   success: true;
   data: string;
}
interface Failure {
   success: false;
   message: string;
}

type Response = Success | Failure;
function handleResponse(response: Response) {
if (response.success) {
   console.log(response.data);
} else {
console.error(response.message);
}
}
const successResponse: Success = { success: true, data: "Hello, world!" };
const failureResponse: Failure = { success: false, message: "Something went wrong." };
handleResponse(successResponse); // Output: Hello, world!
handleResponse(failureResponse); // Output: Something went wrong.

Example 3: Discriminated union react conditional props

We can also used descriminated union to used conditional props in react functional component. In TypeScript, when using Discriminated Unions, it’s important to handle all possible types in the union. Here is an example

type ComponentPropsBase = {
  commonProp: string;
};

type WithMessageProps = ComponentPropsBase & {
  hasData: true;
  message: string;
};

type WithoutMessageProps = ComponentPropsBase & {
  hasData: false;
  noDataProp: string; // Additional prop specific to the case where there is no data
};

type MyComponentProps = WithMessageProps | WithoutMessageProps;

const MyComponent: React.FC<MyComponentProps> = (props) => {
  if (props.hasData) {
    return (
      <div>
        <p>{props.commonProp}</p>
        <p>{props.message}</p>
      </div>
    );
  } else {
    return (
      <div>
        <p>{props.commonProp}</p>
        <p>{props.noDataProp}</p> {/* Render additional prop specific to the case where there is no data */}
      </div>
    );
  }
};

We havenoDataProp to the WithoutMessageProps type to represent additional props specific to the case where there is no data. Then, in the MyComponent function component

Example 4 Discriminated union react props

Here we have component to show message Alert or Cautious . The cautious props type have extra data called level, now we can called this component in parent component where we can have it props value mode, children are compulsary and level is optional. We need level in props only when we have mode is caution.

import { ReactNode } from "react";

type AlertBoxProps = {
  mode: "alert";
  children: ReactNode;
};

type CautionBoxProps = {
  mode: "caution";
  level: "low" | "medium" | "high";
  children: ReactNode;
};
type MessageBoxProps = AlertBoxProps | CautionBoxProps;
//Now create third props type what is either one of this two

function MessageBox(props: MessageBoxProps) {
  const { children, mode } = props;  
   //Otherwise error if we descructure the pros as level is not exist in some cases

  if (mode === "alert") {
    return (
      <aside className="messagebox messagebox-alert">
        <p>{children}</p>
      </aside>
    );
  }

  if (mode === "caution") {
    const { level } = props as CautionBoxProps;
    return (
      <aside className={`messagebox messagebox-caution caution--${level}`}>
        <h2>Caution</h2>
        <p>{children}</p>
      </aside>
    );
  }
  throw new Error(`Unsupported mode: ${mode}`);
}

export default MessageBox;

Here are fews case, where we can use this in react project.

  • Data Fetching: Handling different response types from APIs (success, error, loading).
  • Form Validation: Representing valid or invalid form states with distinct properties.
  • Component State: Modeling different component variations based on internal states or props.
  • Routing: Defining different route configurations for different page types.

Mastering Conditional Props with Discriminated Unions in React TypeScript

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top