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
radiusproperty (number). - Rectangle: Should have
widthandheightproperties (both numbers). - Triangle: Should have
baseandheightproperties (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
Shapetype as a discriminated union. - Each shape variant must have the required properties.
- Implement the
areafunction that correctly calculates the area for each shape. - The
areafunction 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, andbaseproperties must be numbers. - The
typeproperty must be a string literal type. - The
areafunction must return a number. - The code should be well-formatted and readable.
Notes
- The
typeproperty is crucial for discriminated unions. It allows TypeScript to determine the specific shape variant at runtime. - Consider using a
switchstatement or type guards (e.g.,isCircle,isRectangle,isTriangle) within theareafunction 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.PIconstant is available for calculating the area of a circle.