How can I create a compiler error if a variable is NOT of type never?
Let's say I have a list of conditionals and I want to handle every possible value of type
. If I added a new type
in the future and I forget to handle it, I want an error - at least a run-time error, but ideally a compiler error so I catch my mistake before I deploy.
How can I assert that a variable is of type never
?
type Job = { type: 'add'; payload: any } | { type: 'send'; payload: any }
const handleJob = (job: Job) => {
const add = (job: Job) => {
console.log(job)
}
const send = (job: Job) => {
console.log(job)
}
if (job.type === 'add') {
add(job)
} else if (job.type === 'send') {
send(job)
} else {
// `job` is actually type `never` here,
// this error won't ever be thrown unless additional strings are added to the `Job.type` schema.
throw new Error(`Unhandled job.type "${(job as Job).type}".`)
}
}
Answers
To ensure that you handle every possible value of the type
property in your Job
type and receive a compiler error if you forget to handle a new type in the future, you can use exhaustiveness checking in TypeScript.
One way to achieve this is by using a switch statement with an exhaustive check at the end using a TypeScript feature called exhaustiveness checking. Here's how you can modify your handleJob
function to achieve this:
type Job = { type: 'add'; payload: any } | { type: 'send'; payload: any }
const handleJob = (job: Job) => {
const add = (job: Job) => {
console.log(job)
}
const send = (job: Job) => {
console.log(job)
}
switch (job.type) {
case 'add':
add(job);
break;
case 'send':
send(job);
break;
default:
// This line will produce a compiler error if there's a new type added to Job
// that is not handled in the switch statement
const _exhaustiveCheck: never = job;
throw new Error(`Unhandled job.type "${job.type}".`);
}
}
In this code:
- We use a switch statement to handle each possible value of the
type
property. - The
default
case contains a TypeScript assertion thatjob
is of typenever
. This ensures that TypeScript will produce a compiler error if there's a new type added toJob
that is not handled in the switch statement. - If the
default
case is reached, it means thatjob
has a type that is not explicitly handled, and an error is thrown.
This approach provides safety against forgetting to handle new types in the future, as TypeScript will give you an error during compilation if you miss handling any case.