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:
- Define Shape Types: Create distinct interfaces for
Circle,Rectangle, andTriangle. - 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"). - Area Calculation Function: Implement a function,
calculateArea(shape: Shape): number, that takes anyShapeas 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
Circlewith radiusrshould have an area ofπ * r^2. - A
Rectanglewith widthwand heighthshould have an area ofw * h. - A
Trianglewith baseband heighthshould have an area of0.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
Shapetype must be a discriminated union. - The discriminant property must be a string literal (e.g.,
"circle","rectangle","triangle"). - The
calculateAreafunction must be robust enough to handle valid and invalid dimensions as described in the examples. - Assume
Math.PIis available for calculations.
Notes
- Think about how TypeScript's type narrowing works with discriminated unions. This will be key to implementing the
calculateAreafunction safely. - For the triangle, we'll simplify the problem by only considering base and height, not all three side lengths.
- Consider using a
switchstatement or a series ofif/else ifstatements withincalculateAreato handle each shape variant.