Hone logo
Hone
Problems

Advanced Mapped Types: Safe Property Access and Conditional Transformations

Mapped types are a powerful feature in TypeScript that allow you to transform existing types into new ones. This challenge focuses on leveraging advanced mapped types to create a utility type that ensures safe property access on objects and conditionally transforms properties based on their types. This is useful for creating more robust and type-safe code, especially when dealing with potentially undefined or nullable properties.

Problem Description

You need to create a TypeScript utility type called SafePropertyAccess<T> that takes a type T as input and returns a new type where:

  1. Safe Property Access: For each property K in T, the resulting type should have a property K of type T[K] | undefined. This ensures that accessing a property will never throw an error due to undefined values, as the type explicitly allows for it.
  2. Conditional Transformation (Optional): If a property K in T is of type string | number, the corresponding property in the output type should be of type T[K] | null. This demonstrates conditional transformation based on property type. If the property is not a string or number, it retains its original type.

Essentially, SafePropertyAccess<T> should make accessing properties of type T safer by explicitly allowing undefined (and conditionally null for string/number properties) and demonstrate conditional type transformations.

Examples

Example 1:

Input:
type MyType = {
  name: string;
  age: number;
  isActive: boolean;
};

Output:
type SafeMyType = SafePropertyAccess<MyType>;

// SafeMyType should be:
// {
//   name: string | undefined;
//   age: number | undefined;
//   isActive: boolean | undefined;
// }

Explanation: All properties are made safe by adding | undefined.

Example 2:

Input:
type AnotherType = {
  id: number;
  title: string;
  description: string;
  price: number;
  metadata: {
    version: string;
    author: string;
  }
};

Output:
type SafeAnotherType = SafePropertyAccess<AnotherType>;

// SafeAnotherType should be:
// {
//   id: number | undefined;
//   title: string | undefined;
//   description: string | undefined;
//   price: number | undefined;
//   metadata: {
//     version: string | undefined;
//     author: string | undefined;
//   } | undefined;
// }

Explanation: id, title, description, price and metadata properties are made safe.

Example 3: (Edge Case - Nested Type)

Input:
type NestedType = {
  data: {
    value: string;
  }
};

Output:
type SafeNestedType = SafePropertyAccess<NestedType>;

// SafeNestedType should be:
// {
//   data: {
//     value: string | undefined;
//   } | undefined;
// }

Explanation: The nested data property and its value property are both made safe.

Constraints

  • The input type T can be any valid TypeScript type, including object types, primitive types, unions, and intersections.
  • The conditional transformation (string/number to null) is optional. The core requirement is the safe property access (adding | undefined).
  • The solution must be a valid TypeScript type definition.
  • The solution should be concise and efficient, leveraging TypeScript's type system effectively.

Notes

  • Consider using conditional types and mapped types to achieve the desired transformation.
  • The keyof operator will be useful for iterating over the properties of the input type.
  • Think about how to conditionally transform properties based on their type using typeof or similar techniques within the mapped type.
  • Remember that mapped types are evaluated at compile time, so there's no runtime overhead. The goal is to ensure type safety during development.
Loading editor...
typescript