Explaining the video "Enums Considered Harmful" of Matt Pocock
Posted on December 18, 2022.
I was watching this video by Matt Pocock, but I found really hard to digest and internalize the TypeScript, so I decided to create a little guide, because I learn better when I explain things to others, so here it is. The TypeScript shown in the video
const LOG_LEVEL = {
DEBUG: 'debug',
WARNING: 'warning',
ERROR: 'error',
} as const;
type ObjectValues<T> = T[keyof T];
type LogLevel = ObjectValues<typeof LOG_LEVEL>
Destructuring…
‘typeof’ will get you the types
type LOG_LEVEL_TYPE = typeof LOG_LEVEL;
// Hardcoded will be like this:
type LOG_LEVEL_TYPE_2 = {
readonly DEBUG: "debug";
readonly WARNING: "warning";
readonly ERROR: "error";
}
‘keyof’ will get the keys
type LOG_LEVEL_KEYS = keyof LOG_LEVEL_TYPE;
// Hardcoded will be like this:
type LOG_LEVEL_KEYS_2 = "ERROR" | "WARNING" | "DEBUG"
‘type[i]’ will get the values
type LOG_LEVEL_VALUES = LOG_LEVEL_TYPE["ERROR" | "WARNING" | "DEBUG"];
// or
type LOG_LEVEL_VALUES_2 = LOG_LEVEL_TYPE[LOG_LEVEL_KEYS];
// Hardcoded will be like this:
type LOG_LEVEL_VALUES_3 = "debug" | "warning" | "error"
For example the variable myLog can only be the VALUES, anything else will throw an error
let myLog: LOG_LEVEL_VALUES;
myLog = 'debug';
myLog = 'warning';
myLog = 'error';
MyLog_2 can only be the KEYS
let myLog_2: LOG_LEVEL_KEYS;
myLog_2 = 'DEBUG';
myLog_2 = 'ERROR';
myLog_2 = 'WARNING';
The previous can also be compressed and written like this:
type LOG_LEVEL_VALUES_4 = typeof LOG_LEVEL[keyof typeof LOG_LEVEL];
// Hardcoded will be like this:
type LOG_LEVEL_VALUES_5 = "debug" | "warning" | "error"
Now explaining the TypeScript will be:
type ObjectValues<T> = T[keyof T];
// Imagine it as a function that returns the values of wherever T is.
function ObjectValues(T:any) {
return T[Object.keys(T)[0]]
}
for example:
type MyType = {
myString: 'my string',
myNumber: 23,
}
let myValue:ObjectValues<MyType>;
myValue = 'my string' // can only be the litteral string,
myValue = 23; // or the litteral value
Explaining the last part
type LogLevel = ObjectValues<typeof LOG_LEVEL>
// Now imagine this part like we actually execute the function and
// we passed the types provided by "typeof"
let logLevel = ObjectValues({ myString: 'my string'})