Skip to content

Instantly share code, notes, and snippets.

@ebeloded
Last active November 7, 2024 09:02
Show Gist options
  • Save ebeloded/08f7152acc297571c20b8a9d0de41170 to your computer and use it in GitHub Desktop.
Save ebeloded/08f7152acc297571c20b8a9d0de41170 to your computer and use it in GitHub Desktop.
NestedKeys & AtPath utility types
type NestedKeys<T> = T extends object
? {
[K in keyof T]-?: K extends string
? T[K] extends infer U
? U extends object
? K | `${K}.${NestedKeys<U>}`
: K
: never
: never;
}[keyof T]
: "";
type AtPath<T, Path extends string> = Path extends `${infer Head}.${infer Rest}`
? Head extends keyof T
? AtPath<NonNullable<T[Head]>, Rest>
: never
: Path extends keyof T
? T[Path]
: never;
type AccountMoneyAmount = {
account: string;
currency: string;
amount: number;
another: {
level: number;
};
};
interface Transaction {
type: string;
from: AccountMoneyAmount | null;
to: AccountMoneyAmount | null;
date: string;
dateTime: string;
category: string | null;
map: {
[key: string]: {
hello: string;
};
};
}
type TransactionObjectPath = NestedKeys<Transaction>;
// Valid
let a: TransactionObjectPath = "type"; // valid
let b: TransactionObjectPath = "from.account"; // valid
let c: TransactionObjectPath = "to.amount"; // valid
// Invalid
let x: TransactionObjectPath = "xyz"; // not valid
type TransactionObjectPathValue = AtPath<Transaction, "from">;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment