I want to create a type NestedKeys
that iterates over the given nested type RootNav
and collects all keys where the value is Nested<T>
and make this a union type of strings containing the keys, while following the nested structure (maybe recursive?)
type Nav = {
[key: string]: NestedNav<Nav> | object | undefined
}
type NestedNav<T extends Nav> = T
type RootNav = {
LoginNav: NestedNav<LoginNav>;
RegistrationNav: NestedNav<RegistrationNav>;
AppNav: NestedNav<AppNav>
}
type AppNav = {
MainNav: NestedNav<MainNav>;
FooScreen: undefined
BarScreen: {id: string}
};
type LoginNav = {
LoginScreen: undefined
}
type RegistrationNav = {
RegistrationScreen: undefined
}
type MainNav = {
HomeScreen: undefined
ProfileScreen: undefined
}
The endresult should be
type NestedKeys<RootNav>
// → "RootNav" | "LoginNav" | "RegistrationNav" | "AppNav" | "MainNav"
I had something in mind like this, but don't know how to do it properly. This doesn't work:
type NestedKeys<T extends Nav> = T[keyof T] extends NestedNav<any> ? NestedKeys<T[keyof T]> : T```
It is possible to do but it requires a small refactor of types.
TypeScript does not support macros
. There is no concept of value.toString
like in javascript. It means that having some type you are unable to get string representation of type name.
That's why I have added tag
property:
type Prefix = `${string}Nav`
type Nav =
& Record<'tag', Prefix>
& {
[key: Prefix]: undefined | Nav
}
type NestedNav<T extends Nav> = T
type RootNav = {
tag: 'RootNav'
LoginNav: NestedNav<LoginNav>;
RegistrationNav: NestedNav<RegistrationNav>;
AppNav: NestedNav<AppNav>
}
type AppNav = {
tag: 'AppNav'
MainNav: NestedNav<MainNav>;
FooScreen: undefined
BarScreen: { id: string }
};
type LoginNav = {
tag: 'LoginNav';
LoginScreen: undefined
}
type RegistrationNav = {
tag: 'RegistrationNav';
RegistrationScreen: undefined
}
type MainNav = {
tag: 'MainNav';
HomeScreen: undefined
ProfileScreen: undefined
}
type DefaultTag<T> = T extends { tag: infer Tag } ? Tag : never
type GetNames<T, Cache extends any[] = [DefaultTag<T>]> =
(T extends string
? Cache[number]
: {
[Prop in keyof T]:
(T[Prop] extends { tag: infer Tag }
? GetNames<T[Prop], [...Cache, Tag]>
: GetNames<T[Prop], Cache>)
}[keyof T]
)
type Result = GetNames<RootNav>
type Prefix
represents any nav name.
type Nav
represents valid nav type.
type GetNames
iterates recursively through nav
type and adds tag
property to Cache
if such exists.