TypeScript offers never data type for values that never occur. I’ve never seen it in my work but I found it useful for some cases. Let’s see how to use never type.
Robust switch case with Enum
I often see like the following code.
enum MyEnum {
A,
B,
}
function doSwitch(value: MyEnum): boolean {
switch (value) {
case MyEnum.A: return true;
case MyEnum.B: return true;
default:
throw new Error("This error shouldn't occur.");
}
}
A function requires a value and it returns a value or object according to the input. This switch case covers all cases at the moment. However, we need to add new value to the new feature. It means that we need to adjust the doSwitch function as well. But imagine that the MyEnum is used in many places. In this case, we may forget to adjust the function.
enum MyEnum {
A,
B,
C, // new value is added
}
function doSwitch(value: MyEnum): boolean {
switch (value) {
case MyEnum.A: return true;
case MyEnum.B: return true;
// forget to add new case statement
default:
// program reaches here when the value is MyEnum.C
throw new Error("This error shouldn't occur.");
}
}
We may find the error at runtime but its bug may not be detected if it is not a thorough test.
We can avoid this bug by using never data type. Look at the following code.
enum MyEnum {
A,
B,
}
function doSwitch(value: MyEnum): boolean {
switch (value) {
case MyEnum.A: return true;
case MyEnum.B: return true;
default: {
// Use never type here
const exhaustiveCheck: never = value;
throw new Error(exhaustiveCheck);
}
}
}
never type is used in the default statement. The program never reaches there because MyEnum has only two values A and B. When adding new value C, the compiler tells us that there’s something wrong with the function.
enum MyEnum {
A,
B,
C,
}
function doSwitch(value: MyEnum): boolean {
switch (value) {
case MyEnum.A: return true;
case MyEnum.B: return true;
default: {
const exhaustiveCheck: never = value;
// Type 'MyEnum' is not assignable to type 'never'.ts(2322)
// const exhaustiveCheck: never
throw new Error(exhaustiveCheck);
}
}
}
We can definitely recognize that we have to make a change for this function.
We can apply this technique to if-else statement as well.
function doIf(value: MyEnum) {
if (value === MyEnum.A) { return true; }
else if (value === MyEnum.B) { return true; }
else if (value === MyEnum.C) { return true; }
else {
const exhaustiveCheck: never = value;
throw new Error(exhaustiveCheck);
}
}
Comments