ARTICLE AD BOX
This is discussed in microsoft/TypeScript#33025. The short answer is that this is intentional behavior to make keyof never consistent with the rules for keyof. Since keyof T is contravariant in T, and that since never has no values at all, keyof never should have all values. So keyof never is the union of all possible key types, meaning string | number | symbol.
Longer answer:
The keyof operator is naturally contravariant in its argument, meaning that T extends U implies keyof U extends keyof T and vice versa. Notice how the positions of T and U swap; keyof X varies in the opposite direction as X varies. They "counter-vary", hence "contravariant".
Contravariance turns unions into intersections and vice versa. So the general rules are:
keyof (T & U) = keyof T | keyof U: an intersection of object types like {a: string} & {b: number} looks like a single object type with all the properties like {a: string; b: number}. So naturally the keys of the intersection will be the union of the keys of each member of the intersection. keyof (T | U) = keyof T & keyof U: a union of object types like {a: string, c: string} | {b: number, c: number} behaves sort of like a single object type with just the common properties like {c: string | number}. Until you can figure out which member of the union an object corresponds to, the only safe properties to check are the ones that exist in all the union members. (See the highlighted handbook text for more information.) So naturally the keys of the union will be the intersection of the keys of each member of the union.So whatever keyof never is, it should obey these rules.
The never type is TypeScript's bottom type, meaning it's the narrowest possible type. It's the intersection of every type. There are no values of this type; it's uninhabited; you will "never" see a value of this type. For any type T:
T | never is just T (meaning never gets absorbed into unions); and T & never is just never (meaning never absorbs other members in intersections).Combining the previous rules implies that
keyof (T | never) = keyof T = keyof T & keyof never. So keyof never needs to be something that gets absorbed into all intersections. That would have to be the top type for keys, so it has to be any possible key, like string | number | symbol. (It could be the unknown type, which is the top type for all of TypeScript, but that's wider than we need for keys.)
keyof (T & never) = keyof never = keyof T | keyof never. So keyof never needs to be something that absorbs all unions. Again, that means it has to be the top type for keys.
Both rules give us the requirement (or at least the strong suggestion) that keyof never should be the widest possible type it can be, which for keys is just string | number | symbol. (See support for number and symbol keys in mapped types for the rules around this).
Since keyof is contravariant, and since never is the narrowest possible type you can get, keyof never should should be the widest possible key type you can get.
