
问题所在
下面的代码是一个计数器组件的简单还原器。它包含一个在联合类型上的switch 语句,这是相当安全的类型:
type Increment = {
type: "increment";
incrementStep: number;
};
type Decrement = {
type: "decrement";
decrementStep: number;
};
type Actions = Increment | Decrement;
const reducer = (state: State, action: Actions): State => {
switch (action.type) {
case "increment":
return { count: state.count + action.incrementStep };
case "decrement":
return { count: state.count - action.decrementStep };
}
};
我们怎样才能使这个类型更加安全呢?如果我们有一个新的要求,要重置计数器,怎么办?这将意味着一个新的动作被添加到Actions 类型中。当我们添加新的动作时,如何让TypeScript编译器提醒我们需要实现switch 语句中的一个新分支?
解决方案
如果我们可以告诉TypeScript编译器,switch 语句中的default 分支不应该被达到呢?好吧,我们可以用never 类型来做到这一点!
让我们研究一下action 在default 分支中的类型是什么:
const reducer = (state: State, action: Actions): State => {
switch (action.type) {
case "increment":
return { count: state.count + action.incrementStep };
case "decrement":
return { count: state.count - action.decrementStep };
default:
action;
}
};

它的类型是never!
因此,如果我们创建一个假函数,接收一个类型为never 的参数,在default switch 分支中调用,也许TypeScript编译器会在到达那里时出错......
const reducer = (state: State, action: Actions): State => {
switch (action.type) {
case "increment":
return { count: state.count + action.incrementStep };
case "decrement":
return { count: state.count - action.decrementStep };
default:
neverReached(action);
}
};
const neverReached = (never: never) => {};
让我们添加一个Reset 动作并试一试:
type Reset = {
type: "reset";
to: number;
};
type Actions = Increment | Decrement | Reset;

TypeScript编译器如我们所愿出错。很好!
结束语
调用一个假函数,它的参数类型是never ,这是一个很好的方法,可以让TypeScript编译器在我们的程序到达一个不应该到达的区域时发出警告。在联盟类型的switch 语句中,这是一个很好的提示,这样我们就会被提醒在联盟类型被改变时实现一个额外的分支。
如果你想了解更多关于never 类型的信息,Marius Schulz 有一篇很棒的文章。