never型って?
typeScriptにおけるbottom
型です。
bottom型って何よ
型理論、数理論理学において値を持たない型のことである
↑wikiからの抜粋です。
値を持たない型, これだけだと???となりそうですが、
戻り値の型がボトム型である関数は、いかなる値も返さない。
こちらの説明だと少しわかりやすいかと思います。
つまり、
const foo = () => {
while(true){}
}
上記のようにwhile(true)
で戻り値がない場合(= returnが絶対に走らない場合)などは、関数foo
の戻り値の型はnever
と言えます。
特徴
never
は値のない型なので、値を入れるとエラーになります。
let foo:never
foo = 'test' // コンパイルerror
実際に戻り値がneverとなる例
上記でwhileの例を上げましたが、他の例とも合わせて再掲します。
// 無限ループする場合
const foo = () => {
while(true){}
}
// errorをthrowする場合
const bar = () => {
throw new Error("これはエラーです")
}
void
とnever
の違いは?
これは実際にコードを見てもらった方がわかりやすいです。
const foo = () => {
}
const bar = () => {
return
}
const baz = () => {
while(true){}
}
let fooVar:never = foo() // error
let barVar:never = bar() // error
let bazVar:never = baz() //ok!
上記では baz
のみ戻り値がnever型
で、 foo
とbar
の戻り値はvoid型
です。
JSに置いてreturn文
を省略された場合はundefined
が返る仕様なので、
foo
とbar
は実質的に同じです。
voidはreturnでの戻り値なし!の型なのに対して
neverはそもそもreturnしない。
似ているようで全然違っています。
実際での使用例
値を入れることができない という部分が生きてきます。
下記のような型がそれぞれあったとします。
interface Neko {
type: "neko"
}
interface Inu {
type: "inu"
name: number
}
interface Other {
type: "other"
}
type Animal = Neko | Inu | Other
上記Animal型を使う上で、type
によってそれぞれ別の処理をさせたい場合、
下記のようなコードになるかと思います。
function intro(s: Animal) {
if (s.type === "neko") {
return `吾輩は猫である。名前はまだない`
}
else if (s.type === "inu") {
return `${name}は犬のお廻りです。`
}
}
上記ではNeko
Inu
の型しか想定されていません。
Other
型, またそれ以外でも想定しない型が渡ってきた場合にerrorを返す仕組みがあれば便利です。
そこで、予期しない型が渡ってきた場合はnever
に代入することでコンパイルエラーを出し、
網羅漏れに気づくことができます。
(全て網羅できていれば、 _exhaustiveCheck
はnever型となります。)
function intro(s: Animal) {
if (s.type === "neko") {
return `吾輩は猫である。名前はまだない`
}
else if (s.type === "inu") {
return `${name}は犬のお廻りです。`
}
else {
// Otherが渡ってきた場合、neverの特徴によってerrorが出て気づくことができる。
const _exhaustiveCheck: never = s;
}
}
上記の手法はswitch
のdefault文に置いても有効ですね。
参考文献
TypeScript Deep Dive 日本語版
Use the never type to avoid code with dead ends using TypeScript
TypeScriptの型入門