Last active
February 23, 2024 16:17
-
-
Save steven-schmoll-at/5b4a886456c7cd9a854bb61f4a2a004a to your computer and use it in GitHub Desktop.
A typescript type to extract the keys and sub-keys of objects as dot separated paths.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type CombineAll<T> = T extends {[name in keyof T]: infer Type} ? Type : never | |
type PropertyNameMap<T, IncludeIntermediate extends boolean> = { | |
[name in keyof T]: T[name] extends object ? ( | |
SubPathsOf<name, T, IncludeIntermediate> | (IncludeIntermediate extends true ? name : never) | |
) : name | |
} | |
type SubPathsOf<key extends keyof T, T, IncludeIntermediate extends boolean> = ( | |
`${string & key}.${string & PathsOf<T[key], IncludeIntermediate>}` | |
) | |
/** | |
* Extracts out the keys of the given type as dot separated paths. | |
* For example: | |
* { | |
* key1: string; | |
* key2: { | |
* sub1: string; | |
* sub2: string | |
* } | |
* } | |
* | |
* Will be extracted as: | |
* 'key1' | 'key2.sub1' | 'key2.sub2' | |
* | |
* If you provide a value of `true` for the IncludeIntermediate parameter, then it will | |
* also include the object keys by themselves: | |
* 'key1' | 'key2' | 'key2.sub1' | 'key2.sub2' | |
* | |
* This works to any depth, so you can have objects within objects, within objects, etc. | |
* | |
* NOTE: You cannot have circular types in this. ie. A child key cannot reference its own | |
* type or a parent type. | |
*/ | |
export type PathsOf<T, IncludeIntermediate extends boolean = false> = CombineAll<PropertyNameMap<T,IncludeIntermediate>> | |
// Example types | |
interface LeafInterface { | |
end: string; | |
finish: string; | |
complete: string; | |
} | |
interface ChildInterface { | |
leaf: LeafInterface; | |
deadEnd: true; | |
} | |
interface ParentInterface { | |
child1: ChildInterface; | |
child2: ChildInterface; | |
child3: ChildInterface; | |
} | |
function onlyPathsOf(key: PathsOf<ParentInterface>) { | |
console.log(key); | |
} | |
function onlyPathsOfWithIntermediaries(key: PathsOf<ParentInterface, true>) { | |
console.log(key); | |
} | |
// Valid | |
onlyPathsOf('child3.deadEnd') | |
onlyPathsOf('child1.leaf.complete') | |
onlyPathsOf('child3.leaf.finish') | |
onlyPathsOfWithIntermediaries('child1.leaf') | |
// Not valid | |
onlyPathsOf('child4.no') | |
onlyPathsOf('child3.leaf') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment