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:
-
isSatisfiedBy<T>(value: any): (type: T) => boolean;- This function takes a value and returns a function. The returned function accepts a type
Tand returnstrueif thevaluesatisfies that type, andfalseotherwise.
- This function takes a value and returns a function. The returned function accepts a type
-
satisfies<T>(type: T): (value: any) => boolean;- This function takes a type
Tand returns a function. The returned function accepts a value and returnstrueif the value satisfies the given type, andfalseotherwise.
- This function takes a type
-
deepSatisfies<T>(value: any): (type: T) => boolean;- This function takes a value and returns a function. The returned function accepts a type
Tand returnstrueif thevaluesatisfies that type, andfalseotherwise. This helper should perform a deep check, meaning it recursively checks the types of nested properties within the object. UsePartialto allow for optional properties.
- This function takes a value and returns a function. The returned function accepts a type
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
isSatisfiedByandsatisfiesfunctions should perform a shallow type check. - The
deepSatisfiesfunction must perform a deep type check, recursively validating nested properties. - The functions should handle cases where the input value is
nullorundefinedgracefully (returningfalseif the type definition does not allow fornullorundefined). - 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
satisfieshelpers. - The
Partialutility type can be helpful for handling optional properties in thedeepSatisfiesfunction. - 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
anytype should be used only when absolutely necessary. Strive to use more specific types whenever possible.