Hone logo
Hone
Problems

Implementing Optional Chaining in TypeScript

Optional chaining is a powerful feature in TypeScript (and JavaScript) that allows you to safely access properties of an object that might be null or undefined, preventing errors. This challenge asks you to implement a simplified version of optional chaining using TypeScript's type system, focusing on the type safety aspects rather than the runtime behavior. You'll be defining types that represent optional property access and ensuring the resulting type accurately reflects the potential for undefined values.

Problem Description

You need to create a utility type called OptionalChain<T, Path> that simulates optional chaining. T represents the base object type, and Path is a tuple of string keys representing the chain of properties to access. The resulting type should be the type of the property at the end of the chain, or undefined if any property in the chain is potentially undefined.

What needs to be achieved:

  • Define a type OptionalChain<T, Path> that takes a base type T and a tuple of string keys Path.
  • The resulting type should be the type of the property at the end of the chain, but with the possibility of being undefined if any property in the chain is potentially undefined.
  • The type should accurately reflect the potential for undefined at each step of the chain.

Key Requirements:

  • The type must be recursive to handle chains of arbitrary length.
  • The type must correctly handle cases where a property in the chain is optional (i.e., its type is T[keyof T]?).
  • The type must return undefined if any property in the chain is potentially undefined.

Expected Behavior:

Given a type Person with optional properties, OptionalChain<Person, ['address', 'street']> should accurately reflect that the resulting type might be string | undefined.

Edge Cases to Consider:

  • Empty Path tuple: Should return T.
  • Path containing a key that doesn't exist in T: Should return undefined.
  • Nested optional properties: OptionalChain<Person, ['address', 'street', 'number']> where address and street are optional.

Examples

Example 1:

type Person = {
  name: string;
  address?: {
    street: string;
    city: string;
  };
};

type AddressStreet = OptionalChain<Person, ['address', 'street']>;

// AddressStreet should be string | undefined

Example 2:

type Product = {
  id: number;
  details?: {
    price?: number;
  };
};

type ProductPrice = OptionalChain<Product, ['details', 'price']>;

// ProductPrice should be number | undefined

Example 3:

type Config = {
  theme?: {
    primaryColor?: string;
  };
};

type ConfigPrimaryColor = OptionalChain<Config, ['theme', 'primaryColor']>;

// ConfigPrimaryColor should be string | undefined

Constraints

  • The solution must be a valid TypeScript type definition.
  • The solution should be as concise and readable as possible.
  • The solution should correctly handle all the expected behaviors and edge cases described above.
  • No runtime code is required; this is purely a type-level challenge.

Notes

Think about how you can use conditional types and mapped types to recursively traverse the Path tuple and determine the type of each property in the chain. Consider how to represent the possibility of undefined at each step. The key is to build the type definition using TypeScript's type system features to accurately reflect the potential for undefined values.

Loading editor...
typescript