typescript - 如果一个变量不是 never 类型,我如何创建一个编译器错误?
假设我有一个条件列表,并且我想处理 type 的每一个可能值。如果将来我添加了一个新的 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}".`)
}
}
答案#1
你可以引入一个只接受 never 类型参数、且不会返回的函数,这意味着编译器只会在它实际上不期望该函数被调用时才会满意。这个函数通常被称为 assertNever()。下面是一种编写它的方式:
function assertNever(x: never, msg?: string): never {
throw new Error(msg ?? "unexpected value " + String(x));
}
如果它确实被调用,那么你将会遇到运行时错误。现在下面的代码按预期进行编译:
if (job.type === 'add') {
add(job)
} else if (job.type === 'send') {
send(job)
} else {
assertNever(job, // <-- okay
`Unhandled job.type "${(job as Job).type}".`
);
}
但是如果我们添加一种新的工作类型:
type Job =
{ type: 'add'; payload: any } |
{ type: 'send'; payload: any } |
{ type: 'explode' }; // add this
现在它失败了,也在预料之中:
if (job.type === 'add') {
add(job)
} else if (job.type === 'send') {
send(job)
} else {
assertNever(job, // error!
// -----> ~~~
// Argument of type '{ type: "explode"; }' is
// not assignable to parameter of type 'never'.
`Unhandled job.type "${(job as Job).type}".`
);
}
编译器错误会警告你尚未处理新类型,希望这能促使你添加另一个 if/else 语句。
代码的 Playground 链接
答案#2
你可以扩展 Error 类来创建一个错误类型,其构造函数期望一个类型为 never 的参数:
class NeverError extends Error {
constructor(check: never) {
super(`NeverError received unexpected value ${check}, type should have been never.`)
this.name = 'NeverError'
}
}
然后,如果代码被执行到,你就抛出错误,并且如果 job 不是 never,它也会引发编译器错误:
if (job.type === 'add') {
add(job)
} else if (job.type === 'send') {
send(job)
} else {
throw new NeverError(job)
}
