I am grappling with the concept of TypeScript's types when incorporating the keyof
type operator on objects.
Check out this example:
type TypeA = { [k: number]: boolean };
type AKey = keyof TypeA;
// ^? type AKey = number
type TypeB = { [k: string]: boolean };
type BKey = keyof TypeB;
// ^? type BKey = string | number
The TypeScript documentation includes a note that reads:
Note that in this case,
keyof { [k: string]: boolean }
results instring | number
— because JavaScript object keys are always coerced to a string, making obj[0] equivalent to obj["0"].
This can be quite puzzling as it may seem contradictory. The expectation is for AKey
to be number (due to coercion to a string), and BKey
to simply be a string since numbers aren't permitted.
In addition, the behavior differs when using Record<>
, possibly due to the usage of in
versus :
:
type Record<K extends string | number | symbol, T> = { [P in K]: T; }
type TypeC = { [k in number]: boolean }; // Record<number, boolean>
type CKey = keyof TypeC;
// ^? type CKey = number
type TypeD = { [k in string]: boolean }; // Record<string, boolean>
type DKey = keyof TypeD;
// ^? type DKey = string
All types allow the use of both numbers and strings as keys, implying that the type definitions do not impact this aspect:
const value: TypeA | TypeB | TypeC | TypeD = {
0: false,
"1": true,
};
If you can shed some light on this confusion surrounding types, I would greatly appreciate it!