Hone logo
Hone
Problems

Advanced Generic Constraints: Shape-Based Filtering

This challenge focuses on leveraging advanced TypeScript generic constraints to create a utility function that filters an array of objects based on a provided shape. This is useful for ensuring data integrity and type safety when dealing with dynamic data structures where you want to validate that objects conform to a specific structure. You'll be using conditional types and mapped types to achieve this.

Problem Description

You need to implement a function called filterByShape that takes an array of objects and a "shape" object as input. The "shape" object defines the expected keys and types of the objects you want to keep in the filtered array. The function should return a new array containing only the objects from the input array that conform to the provided shape. Conforming means that the object has all the keys defined in the shape, and the values associated with those keys match the types specified in the shape.

Key Requirements:

  • Shape Definition: The "shape" object will be used to define the expected structure of the objects. For example, { name: string, age: number } means the object should have a name property of type string and an age property of type number.
  • Type Safety: The function should be fully type-safe. The TypeScript compiler should be able to infer the types of the filtered array based on the provided shape.
  • Filtering Logic: The function should accurately filter the array, keeping only objects that match the shape.
  • Immutability: The function should not modify the original input array. It should return a new array.

Expected Behavior:

The filterByShape function should return a new array containing only the objects from the input array that have all the keys and corresponding types defined in the shape. Objects missing a key or having a value of the wrong type should be excluded from the filtered array.

Edge Cases to Consider:

  • Empty Shape: If the shape object is empty ({}), the function should return a new array containing all the objects from the input array.
  • Shape with Optional Properties: This challenge does not require handling optional properties in the shape. All properties in the shape are considered required.
  • Input Array with Non-Object Elements: The input array may contain elements that are not objects. These elements should be filtered out.
  • Shape with Conflicting Types: If the shape defines conflicting types for the same key (which shouldn't happen in well-typed code, but consider it for robustness), the function should prioritize the type defined in the shape.

Examples

Example 1:

Input:
array: [{ name: "Alice", age: 30 }, { name: "Bob", age: "25" }, { name: "Charlie" }, {age: 40}]
shape: { name: string, age: number }
Output: [{ name: "Alice", age: 30 }]
Explanation: Only the first object has both 'name' (string) and 'age' (number) properties. The second object has age as a string, the third is missing age, and the fourth is missing name.

Example 2:

Input:
array: [{ id: 123, isValid: true }, { id: "456", isValid: false }, { name: "David" }]
shape: { id: number, isValid: boolean }
Output: [{ id: 123, isValid: true }]
Explanation: Only the first object has both 'id' (number) and 'isValid' (boolean) properties. The second object has id as a string, and the third is missing both properties.

Example 3:

Input:
array: [1, "hello", { name: "Eve", age: 22 }, null, { name: "Frank", age: "33" }]
shape: { name: string, age: number }
Output: [{ name: "Eve", age: 22 }]
Explanation: The input array contains non-object elements. Only the first object with the correct shape is included in the output.

Constraints

  • The input array can contain any number of elements (including zero).
  • The shape object will always be a plain JavaScript object.
  • The function must be implemented using TypeScript generic constraints.
  • The function should be performant enough to handle arrays with up to 1000 objects without significant performance degradation.

Notes

  • Consider using conditional types and mapped types to define the shape constraint.
  • Think about how to handle the case where an object is missing a key defined in the shape.
  • The in operator can be helpful for checking if an object has a specific key.
  • Focus on creating a type-safe and reusable solution. The goal is to demonstrate your understanding of advanced TypeScript generics.
Loading editor...
typescript