Hone logo
Hone
Problems

TypeScript Index Access Utilities

This challenge focuses on creating robust and type-safe utilities for accessing properties of objects in TypeScript, mimicking the behavior of typical object property access but with added safety nets. This is useful for building flexible data manipulation tools and ensuring that unintended property accesses don't lead to runtime errors.

Problem Description

You need to implement two utility functions in TypeScript:

  1. get<T, K extends keyof T>(obj: T, key: K): T[K]: This function should safely retrieve the value of a property from an object. It should leverage TypeScript's type system to ensure that the key provided is actually a valid key of the obj.

  2. set<T, K extends keyof T>(obj: T, key: K, value: T[K]): void: This function should safely set the value of a property on an object. Similar to get, it must ensure the key is valid, and the value must be of the correct type for that property. The function should modify the object in place and return void.

Key Requirements:

  • Type Safety: Both functions must be strongly typed using TypeScript generics. The get function should return a value of the correct type corresponding to the accessed property. The set function should accept a value that matches the property's type.
  • Index Signature Enforcement: Utilize TypeScript's keyof operator and indexed access types (T[K]) to enforce that the provided key is a valid property of the obj and that the value in set matches the property's type.
  • Readability and Simplicity: The implementation should be straightforward and easy to understand.

Expected Behavior:

  • For get: If obj is { a: 1, b: 'hello' } and key is 'a', it should return 1. If key is 'b', it should return 'hello'.
  • For set: If obj is { a: 1, b: 'hello' }, key is 'a', and value is 5, the object should become { a: 5, b: 'hello' }. If key is 'b' and value is 'world', the object should become { a: 1, b: 'world' }.

Edge Cases to Consider:

  • null or undefined objects: While TypeScript generics might help prevent passing null or undefined directly if the type T is not explicitly nullable, the functions should ideally handle scenarios where the object itself might be a valid type but could be null or undefined at runtime if the caller provides it. (However, focus on the type-safe aspects first.)
  • Optional properties: If an object has optional properties, the functions should still work correctly.

Examples

Example 1: get function

const myObject = {
  name: "Alice",
  age: 30,
  isActive: true,
};

// Accessing a valid string property
const name = get(myObject, "name"); // name should be of type string and equal to "Alice"

// Accessing a valid number property
const age = get(myObject, "age"); // age should be of type number and equal to 30

// Accessing a valid boolean property
const isActive = get(myObject, "isActive"); // isActive should be of type boolean and equal to true

// Attempting to access an invalid property (should be a TypeScript compile-time error)
// const invalid = get(myObject, "address"); // Error: Argument of type '"address"' is not assignable to parameter of type '"name" | "age" | "isActive"'.

Example 2: set function

const user = {
  id: 101,
  username: "bobsmith",
  email: "bob@example.com",
};

// Setting a valid string property
set(user, "username", "bobby");
// user should now be { id: 101, username: "bobby", email: "bob@example.com" }

// Setting a valid number property
set(user, "id", 102);
// user should now be { id: 102, username: "bobby", email: "bob@example.com" }

// Attempting to set a property with an incorrect value type (should be a TypeScript compile-time error)
// set(user, "id", "103"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'.
// set(user, "username", 123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'.

Example 3: Handling Optional Properties

interface Product {
  id: string;
  name: string;
  description?: string; // Optional property
}

const product: Product = {
  id: "prod-123",
  name: "Gadget",
};

// Getting an optional property that exists (even if undefined)
const description = get(product, "description"); // description should be of type string | undefined and equal to undefined

// Setting an optional property
set(product, "description", "A cool new gadget.");
// product should now be { id: "prod-123", name: "Gadget", description: "A cool new gadget." }

// Getting the now-set optional property
const updatedDescription = get(product, "description"); // updatedDescription should be of type string | undefined and equal to "A cool new gadget."

Constraints

  • The solution must be written entirely in TypeScript.
  • The get and set functions should use generics to achieve type safety.
  • The key parameter in both functions must be constrained to keyof T.
  • The value parameter in the set function must be constrained to T[K].
  • The set function should modify the object in place and return void.

Notes

  • Consider how to correctly define the generic types for both functions to enforce the relationship between the object, the key, and the value.
  • The primary goal is to leverage TypeScript's type system to prevent common runtime errors related to property access at compile time.
  • While runtime checks for null or undefined obj could be added for increased robustness, the focus of this challenge is on the static type checking capabilities of TypeScript.
Loading editor...
typescript