My other answer mentions this but I will reiterate: this sort of type function is recursive in a way that isn't really supported by TypeScript. It works... until it doesn't work (e.g., the compiler gets bogged down or reports circularity errors). So I don't really recommend using this in any production code base.
Anyway, I can modify the other answer's Paths<T>
definition to be Paths<T, V>
which gives a union of tuples representing key paths in an object of type T
where the value pointed to by that path is assignable to type V
. So Paths<T, unknown>
or Paths<T, any>
should give all the paths, while Paths<T, string | number | boolean>
should give you paths that point to "scalar" values.
Here it is:
type Cons<H, T> = T extends readonly any[] ?
((h: H, ...t: T) => void) extends ((...r: infer R) => void) ? R : never
: never;
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]]
type Paths<T, V = unknown, D extends number = 10> = [D] extends [never] ? never :
(T extends V ? [] : never) | (T extends object ? {
[K in keyof T]-?: Cons<K, Paths<T[K], V, Prev[D]>>
}[keyof T] : never);
The idea is that it walks down through the object (up to some maximum depth of 10 or so, by default), and collects all the paths, but only outputs ones where the type is assignable to V
.
If SomeRecord
is this:
type SomeRecord = {
a: { a1: 'a1', a2: 'a2' }, b: { b1: 'b1' },
c: { foo: string, bar: symbol, baz: undefined, qux: null, quux: () => void }
}
(where I've added a c
with some properties, most of which are not "scalars" so they should be excluded,) then the full paths are:
type AllPaths = Paths<SomeRecord>;
// type AllPaths = [] | ["a"] | ["a", "a1"] | ["a", "a2"] | ["b"] | ["b", "b1"] |
// ["c"] | ["c", "foo"] | ["c", "bar"] | ["c", "baz"] | ["c", "qux"] | ["c", "quux"]
and the scalar paths are:
type ScalarPaths = Paths<SomeRecord, string | number | boolean>;
// type ScalarPaths = ["a", "a1"] | ["a", "a2"] | ["b", "b1"] | ["c", "foo"];
Looks like what you want, I think.
Okay, hope that helps; good luck!
Playground link to code