Simplest example
assuming this type
type Foo = { a: number } | { b: string } | { c: boolean };
is it possible to get
type KeysOfFoo = 'a' | 'b' | 'c';
I tried this but it doesn't work
type Failed = keyof Foo; // never
Simplest example
assuming this type
type Foo = { a: number } | { b: string } | { c: boolean };
is it possible to get
type KeysOfFoo = 'a' | 'b' | 'c';
I tried this but it doesn't work
type Failed = keyof Foo; // never
On
Failed is of type never because your Foo type can't have any keys. It's currently set up as an intersection type between 3 completely exclusive types, so there are no valid keys.
If you change from using | to a &, then it'll work as is.
type Foo = { a: number } & { b: string } & { c: boolean }
type a = keyof Foo // 'a' | 'b' | 'c'
Something like
keyof (A | B | C)will result in only the keys that are definitely on an object of typeA | B | C, meaning it would have to be a key known to be in all ofA,B, andC, which is:keyof A & keyof B & keyof C. That is,keyof Tis "contravariant inT". This isn't what you want though (in your case there are no keys in common so the intersection isnever).If you're looking for the set of keys which are in at least one of the members of your union, you need to distribute the
keyofoperator over the union members. Luckily there is a way to do this via distributive conditional types. It looks like this:The
T extends anydoesn't do much in terms of type checking, but it does signal to the compiler that operations onTshould happen for each union member ofTseparately and then the results would be united back together into a union. That meansAllKeys<A | B | C>will be treated likeAllKeys<A> | AllKeys<B> | AllKeys<C>. Let's try it:Looks good! Please note that you should be careful about actually using
KeysOfFooin concert with objects of typeFoo.keyofis contravariant for a reason:It's not safe to index into
foowithkfor the same reason you can't safely index into a value of type{a: number}with"b"... the key might not exist on the object. Obviously you know your use cases better than I do though, so you may well have some legitimate use ofAllKeys<Foo>andFootogether. I'm just saying to be careful.Playground link to code