I have a type that looks like this.
type Focus =
| { sidebar: true }
| { tabbar: true }
| { menu: string }
The whole point of this type structure is the convenience of being able to do things like if (focus.sidebar)
or if (focus.menu == "open")
so I don't want to rewrite my code as if ("sidebar" in focus)
and if ("menu" in focus && focus.menu === "open")
.
This type would technically work for me:
type Focus =
| { sidebar: true; tabbar?: undefined; menu?: undefined }
| { tabbar: true; sidebar?: undefined; menu?: undefined }
| { menu: string; sidebar?: undefined; tabbar?: undefined }
But this is really inconvenient to write.
I've made it this far:
type PartialEmpty<T> = { [A in keyof T]?: undefined }
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never
type Focus2 = PartialEmpty<UnionToIntersection<Focus>>
// {
// sidebar?: undefined;
// tabbar?: undefined;
// menu?: undefined;
// }
But I can't figure out how to intersect and distribute that type over the union... This is pretty close:
type Distribute<T> = T extends any ? Omit<T, keyof Focus2> & Focus2 : never
type Focus3 = Distribute<Focus>
But it doesn't quite work...
const focus: Focus3 = {} as any
if (focus.menu) {
focus.m
}
CodePudding user response:
T
in Distribute<T>
is one of:
| { sidebar: true }
| { tabbar: true }
| { menu: string }
So you need to omit keyof T
from Focus2
and intersect with T
:
type Distribute<T> = T extends any ? Omit<Focus2, keyof T> & T : never