Ulzurrun de Asanza i Sàez

Month: March 2025

How to write type-safe nested key paths in TypeScript

Recently I had to write a type-safe function that allowed to update (nested) paths of a JavaScript object. I wanted the function to prevent setting a wrong value to the requested path, even nested ones with optional values.

After playing a bit with TypeScript type system I came up with these types which I think can be useful and will be using for sure in future projects. You can play with it in this playground or see the code right below:

export type KeyPath<
    T extends string,
    K extends string,
    Separator extends string = ".",
> = `${T}${"" extends T ? "" : Separator}${K}`;

export type KeyPaths<T extends object, P extends string = "", Separator extends string = "."> = {
    [K in keyof T]-?: K extends string
        ? NonNullable<T[K]> extends object
            ? KeyPath<P, K, Separator> | KeyPaths<NonNullable<T[K]>, KeyPath<P, K, Separator>, Separator>
            : KeyPath<P, K, Separator>
        : never;
}[keyof T & string];

export type GetTypeAtKeyPath<T, Path extends string, Separator extends string = "."> = Path extends keyof T
    ? T[Path]
    : Path extends `${infer K}${Separator}${infer Rest}`
      ? K extends keyof T
          ? NonNullable<T[K]> extends object
              ? GetTypeAtKeyPath<NonNullable<T[K]>, Rest, Separator>
              : never
          : never
      : never;
Code language: TypeScript (typescript)

Let’s dig into how to use it and how KeyPath, KeyPaths and GetTypeAtKeyPath work and how to use it.

Read more →