Hone logo
Hone
Problems

Type Predicates: Refining Type Safety in TypeScript

Type predicates are a powerful feature in TypeScript that allow you to narrow down the type of a variable based on a runtime check. They're particularly useful when dealing with union types or when you need to ensure a value conforms to a specific structure. This challenge will guide you through implementing and utilizing type predicates to enhance type safety and code clarity.

Problem Description

You are tasked with creating and using type predicates in TypeScript to refine the type of variables based on their properties. Specifically, you'll be working with a union type representing different shapes (Circle and Rectangle) and creating a type predicate function to determine if a given object is a Circle. The goal is to write a function that takes a generic object and returns a boolean indicating whether it's a Circle, and then use this predicate to safely access properties specific to the Circle type.

What needs to be achieved:

  1. Define a union type Shape that can be either a Circle or a Rectangle. Circle has a radius property (number), and Rectangle has width and height properties (both numbers).
  2. Create a type predicate function isCircle that accepts a generic object and returns true if the object is a Circle (i.e., has a radius property), and false otherwise.
  3. Write a function calculateArea that takes a Shape and, using the isCircle predicate, calculates the area if it's a circle or returns undefined if it's a rectangle.

Key Requirements:

  • The isCircle function must be a type predicate. This means its return type must be obj is Circle.
  • The calculateArea function must correctly calculate the area of a circle using the radius property and return undefined for rectangles.
  • Type safety is paramount. The code should leverage the type predicate to ensure that properties are accessed only when the type is confirmed.

Expected Behavior:

  • isCircle({ radius: 5 }) should return true.
  • isCircle({ width: 10, height: 20 }) should return false.
  • calculateArea({ radius: 5 }) should return approximately 78.54.
  • calculateArea({ width: 10, height: 20 }) should return undefined.

Edge Cases to Consider:

  • Objects with unexpected properties. The isCircle predicate should only check for the existence of the radius property.
  • Null or undefined input. While not explicitly required, consider how your code handles these cases gracefully (e.g., by returning false from isCircle if the input is null/undefined).

Examples

Example 1:

Input: { radius: 5 }
Output: true
Explanation: The object has a 'radius' property, so it's considered a Circle.

Example 2:

Input: { width: 10, height: 20 }
Output: false
Explanation: The object does not have a 'radius' property, so it's not a Circle.

Example 3:

Input: { radius: 5 }
Output: 78.54 (approximately)
Explanation: The calculateArea function correctly identifies the input as a Circle and calculates its area.

Constraints

  • The radius property of a Circle must be a number.
  • The width and height properties of a Rectangle must be numbers.
  • The calculateArea function should return undefined if the input is a Rectangle.
  • The area calculation should be reasonably accurate (within a small margin of error due to floating-point arithmetic).

Notes

  • Remember that type predicates are functions that return a boolean and have a return type annotation of the form obj is Type.
  • Consider using the typeof operator to check for the existence of a property.
  • Think about how to leverage the type predicate within the calculateArea function to safely access the radius property only when it's confirmed that the object is a Circle.
Loading editor...
typescript