Hone logo
Hone
Problems

Modeling Shapes with Discriminated Unions in TypeScript

Algebraic Data Types (ADTs) are a powerful way to represent data that can take on one of several distinct forms. In TypeScript, discriminated unions provide a type-safe and expressive way to implement ADTs. This challenge will guide you through creating and utilizing discriminated unions to model geometric shapes.

Problem Description

Your task is to create a TypeScript discriminated union that can represent different geometric shapes: a Circle, a Rectangle, and a Triangle. Each shape should have its specific properties (e.g., radius for a circle, width and height for a rectangle, side lengths for a triangle). You will then implement a function that calculates the area of any given shape, demonstrating the power of type checking and pattern matching with discriminated unions.

Key Requirements:

  1. Define Shape Types: Create distinct interfaces for Circle, Rectangle, and Triangle.
  2. Discriminated Union: Combine these interfaces into a single discriminated union type, Shape. Each variant of the union must have a common literal property (the "discriminant") to distinguish it from others (e.g., kind: "circle").
  3. Area Calculation Function: Implement a function, calculateArea(shape: Shape): number, that takes any Shape as input and returns its calculated area.

Expected Behavior:

The calculateArea function should correctly compute the area based on the type of shape it receives. For example:

  • A Circle with radius r should have an area of π * r^2.
  • A Rectangle with width w and height h should have an area of w * h.
  • A Triangle with base b and height h should have an area of 0.5 * b * h.

Edge Cases to Consider:

  • Input shapes might have zero or negative dimensions. Your area calculation should handle these gracefully (e.g., returning 0 for invalid dimensions).

Examples

Example 1:

Input:
{
  kind: "circle",
  radius: 5
}

Output:
78.53981633974483

Explanation: The input is a circle with a radius of 5. The area is calculated as π * 5^2.

Example 2:

Input:
{
  kind: "rectangle",
  width: 10,
  height: 4
}

Output:
40

Explanation: The input is a rectangle with width 10 and height 4. The area is calculated as 10 * 4.

Example 3:

Input:
{
  kind: "triangle",
  base: 6,
  height: 8
}

Output:
24

Explanation: The input is a triangle with base 6 and height 8. The area is calculated as 0.5 * 6 * 8.

Example 4 (Edge Case):

Input:
{
  kind: "rectangle",
  width: -2,
  height: 5
}

Output:
0

Explanation: The input is a rectangle with a negative width. Invalid dimensions result in an area of 0.

Constraints

  • The Shape type must be a discriminated union.
  • The discriminant property must be a string literal (e.g., "circle", "rectangle", "triangle").
  • The calculateArea function must be robust enough to handle valid and invalid dimensions as described in the examples.
  • Assume Math.PI is available for calculations.

Notes

  • Think about how TypeScript's type narrowing works with discriminated unions. This will be key to implementing the calculateArea function safely.
  • For the triangle, we'll simplify the problem by only considering base and height, not all three side lengths.
  • Consider using a switch statement or a series of if/else if statements within calculateArea to handle each shape variant.
Loading editor...
typescript