Hone logo
Hone
Problems

Implementing satisfies Helpers for Enhanced Type Safety in TypeScript

TypeScript's type system is powerful, but sometimes verifying that an object conforms to a specific type definition can be cumbersome. This challenge asks you to implement a set of satisfies helper functions that provide a more concise and readable way to check if a value adheres to a given type. These helpers will be invaluable for validating data structures, ensuring data integrity, and improving code maintainability.

Problem Description

You are tasked with creating a module containing several satisfies helper functions. These functions will take a value and a type as input and return a boolean indicating whether the value satisfies the type. The core functionality revolves around leveraging TypeScript's type inference and conditional types to achieve this.

Specifically, you need to implement the following satisfies helpers:

  1. isSatisfiedBy<T>(value: any): (type: T) => boolean;

    • This function takes a value and returns a function. The returned function accepts a type T and returns true if the value satisfies that type, and false otherwise.
  2. satisfies<T>(type: T): (value: any) => boolean;

    • This function takes a type T and returns a function. The returned function accepts a value and returns true if the value satisfies the given type, and false otherwise.
  3. deepSatisfies<T>(value: any): (type: T) => boolean;

    • This function takes a value and returns a function. The returned function accepts a type T and returns true if the value satisfies that type, and false otherwise. This helper should perform a deep check, meaning it recursively checks the types of nested properties within the object. Use Partial to allow for optional properties.

Examples

Example 1:

Input:
const obj = { name: "Alice", age: 30 };
const type = { name: string, age: number };

const isSatisfied = isSatisfiedBy<typeof type>(obj);
const satisfiesFunc = satisfies<typeof type>();

Output:
isSatisfied(obj) // true
satisfiesFunc(obj) // true

Explanation: The obj satisfies the type definition because it has both name (string) and age (number) properties.

Example 2:

Input:
const obj = { name: "Bob" };
const type = { name: string, age: number };

const isSatisfied = isSatisfiedBy<typeof type>(obj);
const satisfiesFunc = satisfies<typeof type>();

Output:
isSatisfied(obj) // false
satisfiesFunc(obj) // false

Explanation: The obj does not satisfy the type definition because it is missing the age property.

Example 3:

Input:
const obj = { name: "Charlie", details: { age: 25, city: "New York" } };
const type = { name: string, details: { age: number, city: string } };

const deepSatisfied = deepSatisfies<typeof type>(obj);

Output:
deepSatisfied(obj) // true

Explanation: The deepSatisfies function correctly checks the nested details object to ensure it conforms to the expected type.

Constraints

  • All functions must be written in TypeScript.
  • The isSatisfiedBy and satisfies functions should perform a shallow type check.
  • The deepSatisfies function must perform a deep type check, recursively validating nested properties.
  • The functions should handle cases where the input value is null or undefined gracefully (returning false if the type definition does not allow for null or undefined).
  • The functions should not throw errors.
  • Performance should be reasonable for typical object sizes (up to 100 properties deep).

Notes

  • Consider using TypeScript's conditional types and type inference to implement the satisfies helpers.
  • The Partial utility type can be helpful for handling optional properties in the deepSatisfies function.
  • Think about how to handle different types of values (e.g., arrays, primitive types) within the type definitions.
  • Focus on creating clear, concise, and well-documented code. The readability of your solution is as important as its correctness.
  • The any type should be used only when absolutely necessary. Strive to use more specific types whenever possible.
Loading editor...
typescript