Implementing TypeScript Interfaces with At Least One Required Property

Dec 02, 2025 · Programming · 35 views · 7.8

Keywords: TypeScript | Interface | Union Type

Abstract: This article explores strategies for defining TypeScript interfaces that enforce at least one optional property to exist and prevent multiple properties from being set simultaneously. Based on the best answer, it introduces the method of interface splitting and union types, with detailed code examples and logical analysis. Additional methods are briefly compared to aid developers in choosing appropriate solutions.

Problem Background

In TypeScript interface design, it is sometimes necessary to enforce that at least one optional property must be set, such as in a MenuItem interface where either component or click should exist, but not both. This is akin to an XOR operation in logic.

Core Solution: Interface Splitting and Union Types

This requirement can be achieved by splitting interfaces and using union types. First, define a base interface BaseMenuItem that includes all required properties.

export interface BaseMenuItem {
  title: string;
  icon: string;
}

Then, create two extended interfaces that enforce the existence of either component or click property.

export interface ComponentMenuItem extends BaseMenuItem {
  component: any;
}

export interface ClickMenuItem extends BaseMenuItem {
    click: any;
}

Finally, combine these interfaces into a MenuItem type using a union type.

export type MenuItem = ComponentMenuItem | ClickMenuItem;

This ensures that a MenuItem object either has the component property or the click property, but not neither. Attempting to set both properties will result in a TypeScript error.

Code Examples and Analysis

Below is a complete example using this method.

const withComponent: MenuItem = {
  title: "test",
  component: 52,
  icon: "icon"
};

const withClick: MenuItem = {
  title: "test",
  click: 54,
  icon: "icon"
};

// Error: missing component or click
const error: MenuItem = {
  title: "test",
  icon: "icon"
};

// Error: setting both component and click
const errorBoth: MenuItem = {
  title: "test",
  component: 24,
  click: 54,
  icon: "icon"
};

TypeScript's type checker correctly validates these constraints. This approach is simple, readable, and leverages TypeScript's union type features without complex conditional types.

Additional Methods

Beyond interface splitting, conditional types such as RequireAtLeastOne and RequireOnlyOne can be used, but they are more complex and may fail in edge cases. A simpler approach involves unions and intersections, but it might be less strict.

Conclusion

Using interface splitting and union types is recommended due to its clarity, efficiency, and full utilization of TypeScript's type system. For scenarios requiring precise control over property existence, this strategy provides a maintainable solution.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.