Hone logo
Hone
Problems

Representing Shapes with Algebraic Data Types in TypeScript

Algebraic Data Types (ADTs) are a powerful tool for modeling data with distinct variants, offering type safety and clarity. This challenge asks you to implement a system for representing geometric shapes (Circle, Rectangle, and Triangle) using TypeScript's discriminated unions, effectively creating ADTs. This exercise will solidify your understanding of TypeScript's type system and how to leverage ADTs for robust and maintainable code.

Problem Description

You need to create a TypeScript type called Shape that can represent three different geometric shapes: Circle, Rectangle, and Triangle. Each shape variant should have its own specific properties:

  • Circle: Should have a radius property (number).
  • Rectangle: Should have width and height properties (both numbers).
  • Triangle: Should have base and height properties (both numbers).

The Shape type should be a discriminated union, meaning each variant has a unique identifier property that allows TypeScript to narrow down the type safely. You should also create a function area that calculates the area of a given Shape. The area function should use type narrowing based on the shape's identifier to perform the correct calculation.

Key Requirements:

  • Define the Shape type as a discriminated union.
  • Each shape variant must have the required properties.
  • Implement the area function that correctly calculates the area for each shape.
  • The area function should be type-safe and utilize type narrowing.

Expected Behavior:

The area function should return the area of the shape. It should handle all three shape types correctly. The type system should prevent incorrect usage (e.g., trying to access radius on a Rectangle).

Edge Cases to Consider:

  • Negative dimensions (radius, width, height, base). While not strictly required to handle, consider how you might want to address this in a real-world scenario (e.g., throwing an error, returning NaN). For this challenge, you can assume non-negative dimensions.
  • Zero dimensions. These are valid and should be handled correctly.

Examples

Example 1:

Input: area({ type: "circle", radius: 5 })
Output: 78.53981633974483
Explanation: The area of a circle with radius 5 is π * 5^2 ≈ 78.54

Example 2:

Input: area({ type: "rectangle", width: 4, height: 6 })
Output: 24
Explanation: The area of a rectangle with width 4 and height 6 is 4 * 6 = 24

Example 3:

Input: area({ type: "triangle", base: 3, height: 8 })
Output: 12
Explanation: The area of a triangle with base 3 and height 8 is 0.5 * 3 * 8 = 12

Constraints

  • The radius, width, height, and base properties must be numbers.
  • The type property must be a string literal type.
  • The area function must return a number.
  • The code should be well-formatted and readable.

Notes

  • The type property is crucial for discriminated unions. It allows TypeScript to determine the specific shape variant at runtime.
  • Consider using a switch statement or type guards (e.g., isCircle, isRectangle, isTriangle) within the area function to perform type narrowing.
  • Think about how you can leverage TypeScript's type system to ensure type safety and prevent errors. The goal is to create a robust and well-typed solution.
  • The Math.PI constant is available for calculating the area of a circle.
Loading editor...
typescript