DEV Community

Vishesh Tiwari
Vishesh Tiwari

Posted on

How to use Conditional Types in typescript?

Using Conditional Properties in TypeScript: A Practical Example

In TypeScript, conditional properties allow us to create flexible and type-safe interfaces that adapt based on certain conditions. This is particularly useful when dealing with complex data structures where certain properties should only be present under specific circumstances. In this blog post, we’ll explore how to use conditional properties with a practical example involving reward groups.

The Scenario

Imagine we have a system that manages different types of rewards. Each reward can be of a specific type, such as “FINANCE” or “SHIPPING”.

Depending on the reward type, certain attributes should be included or excluded. For instance, financial rewards should include financial attributes, while shipping rewards should include shipping attributes. Additionally, we want to ensure that certain attributes are only included based on the reward type and reward on conditions.

Defining the Types

First, let’s define the basic types and interfaces we’ll be working with:

type RewardType = "FINANCE" | "SHIPPING" | "OTHER"; // Example values for RewardType

interface ItemConditionAttribute {
  // Define the properties of ItemConditionAttribute here
}

interface RewardAttributes {
  // Define the properties of RewardAttributes here
}

interface ShippingAttributes {
  // Define the properties of ShippingAttributes here
}

interface FinanceAttributes {
  // Define the properties of FinanceAttributes here
}

interface RewardGroupBase {
  groupId: number;
  rewardType: RewardType;
  rewardOn: string;
  itemConditionAttributes: ItemConditionAttribute[];
}
Enter fullscreen mode Exit fullscreen mode

Using Conditional Types

To ensure that financeAttributes is only included when rewardType is “FINANCE” and rewardAttributes is not included when rewardOn is “Finance”, we can use conditional types. Here’s how we define the RewardGroup type:

type RewardGroup = RewardGroupBase & (
  { rewardType: "FINANCE"; rewardOn: "Finance"; financeAttributes: FinanceAttributes; rewardAttributes?: never; shippingAttributes?: never } |
  { rewardType: "SHIPPING"; rewardOn: Exclude<string, "Finance">; shippingAttributes: ShippingAttributes; financeAttributes?: never; rewardAttributes: RewardAttributes } |
  { rewardType: Exclude<RewardType, "FINANCE" | "SHIPPING">; rewardOn: Exclude<string, "Finance">; financeAttributes?: never; shippingAttributes?: never; rewardAttributes: RewardAttributes }
);
Enter fullscreen mode Exit fullscreen mode

Explanation

Base Interface:
RewardGroupBase contains the common properties that are always present regardless of the reward type.

Conditional Types:
We use a union of three types to handle the conditional properties.

  • When rewardType is “FINANCE” and rewardOn is “Finance”, financeAttributes is required,
    and rewardAttributes and shippingAttributes are not allowed.

  • When rewardType is “SHIPPING” and rewardOn is not “Finance”, shippingAttributes is required, and financeAttributes is not allowed, but rewardAttributes is included.

  • For any other rewardType and rewardOn not being “Finance”, rewardAttributes is included, but neither financeAttributes nor shippingAttributes are included.

Example Usage

Here’s how you can use the RewardGroup type in practice:


const financeReward: RewardGroup = {
  groupId: 1,
  rewardType: "FINANCE",
  rewardOn: "Finance",
  itemConditionAttributes: [ /* properties */ ],
  financeAttributes: { /* properties */ }
};

const shippingReward: RewardGroup = {
  groupId: 2,
  rewardType: "SHIPPING",
  rewardOn: "Delivery",
  itemConditionAttributes: [ /* properties */ ],
  shippingAttributes: { /* properties */ },
  rewardAttributes: { /* properties */ }
};

// This will cause a TypeScript error because financeAttributes is not allowed for rewardType "SHIPPING"
const invalidReward: RewardGroup = {
  groupId: 3,
  rewardType: "SHIPPING",
  rewardOn: "Delivery",
  itemConditionAttributes: [ /* properties */ ],
  financeAttributes: { /* properties */ } // Error: financeAttributes
};
Enter fullscreen mode Exit fullscreen mode

Top comments (0)