Repository: react-hook-form/react-hook-form
File: src/types/path/eager.ts (TypeScript)

Code before:

/**
 * Helper type for recursively constructing paths through a type.
 * See {@link Path}
 */
type PathImpl<K extends string | number, V, TraversedTypes> = V extends
  | Primitive
  | BrowserNativeObject
  ? `${K}`
  : V extends TraversedTypes
  ? [Extract<V, TraversedTypes>] extends [Extract<TraversedTypes, V>]
    ? `${K}`
    : `${K}` | `${K}.${Path<V, TraversedTypes | V>}`
  : `${K}` | `${K}.${Path<V, TraversedTypes | V>}`;

/**
 * Type which eagerly collects all paths through a type
 * @typeParam T - type which should be introspected
 * @typeParam TraversedTypes - types which have already been traversed to avoid infinite
 * recursion
 * @example
 * ```
 * Path<{foo: {bar: string}}> = 'foo' | 'foo.bar'
 * ```
 */
export type Path<T, TraversedTypes = never> = T extends ReadonlyArray<infer V>
  ? IsTuple<T> extends true
    ? {
        [K in TupleKeys<T>]-?: PathImpl<K & string, T[K], TraversedTypes>;
      }[TupleKeys<T>]
    : PathImpl<ArrayKey, V, TraversedTypes>
  : {
      [K in keyof T]-?: PathImpl<K & string, T[K], TraversedTypes>;
    }[keyof T];

/**
 * See {@link Path}
 */
export type FieldPath<TFieldValues extends FieldValues> = Path<
  UnPackAsyncDefaultValues<TFieldValues>
>;

/**
 * Helper type for recursively constructing paths through a type.
 * See {@link ArrayPath}
 */
type ArrayPathImpl<K extends string | number, V, TraversedTypes> = V extends
  | Primitive
  | BrowserNativeObject
  ? never
  : V extends ReadonlyArray<infer U>

Review comment (suggestion):
Is there a benefit to exposing the `TraversedTypes` type argument to users? I would introduce a type alias to hide it. E.g. `Path<T>` -> `PathAcyclic<T, TraversedTypes>` -> `PathAcyclicImpl<K, T, TraversedTypes>`

Code after:

import { ArrayKey, IsTuple, TupleKeys } from './common';

/**
 * Helper function to break apart T1 and check if any are equal to T2
 *
 * See {@link IsEqual}
 */
type AnyIsEqual<T1, T2> = T1 extends T2
  ? IsEqual<T1, T2> extends true
    ? true
    : never
  : never;

/**
 * Helper type for recursively constructing paths through a type.
 * This actually constructs the strings and recurses into nested
 * object types.
 *
 * See {@link Path}
 */
type PathImpl<K extends string | number, V, TraversedTypes> = V extends
  | Primitive
  | BrowserNativeObject
  ? `${K}`
  : // Check so that we don't recurse into the same type
  // by ensuring that the types are mutually assignable
  // mutually required to avoid false positives of subtypes
  true extends AnyIsEqual<TraversedTypes, V>
  ? `${K}`
  : `${K}` | `${K}.${PathInternal<V, TraversedTypes | V>}`;

/**
 * Helper type for recursively constructing paths through a type.
 * This obsucres the internal type param TraversedTypes from exported contract.
 *
 * See {@link Path}
 */
type PathInternal<T, TraversedTypes = T> = T extends ReadonlyArray<infer V>
  ? IsTuple<T> extends true
    ? {
        [K in TupleKeys<T>]-?: PathImpl<K & string, T[K], TraversedTypes>;
      }[TupleKeys<T>]
    : PathImpl<ArrayKey, V, TraversedTypes>
  : {
      [K in keyof T]-?: PathImpl<K & string, T[K], TraversedTypes>;
    }[keyof T];

/**
 * Type which eagerly collects all paths through a type
 * @typeParam T - type which should be introspected