LoginSignup
3
0

More than 1 year has passed since last update.

TypeScriptでneverを検知する

Posted at

はじめに

TypeScriptを使っているとnever同士比較をしたい時があります。しかし、neverは他の型と異なり少々工夫して比較する必要があります。この記事では型の比較を使用する一例として、引数の型がneverであることを確認する型を解説します。

解説

neverであることを確認するコードは下の通りです。

type IsNever<X> = X[] extends never[] ? true : false;

下のような他の型であることを確認のためのコードは動きません。

type IsNever<X> = X extends never ? true : false;

Conditional Types

まずConditional Typesの仕様を見ていきます。Confitional TypesはTypeScriptの型システムの中で条件型としての役割を担っています。例として下のようなコードを考えます。

type Compare<X> = X extends Y ? true : false;

このコードはXがYに割り当てることが可能な場合にtrueを不可能な場合にfalseを返すようになっています。このような比較して型を抽出することをConditional Typesと言います。条件型としての役割を持つだけあって確かにX extends Yを条件式とした場合三項演算子と似た形式ですね。
さて、このConditional Typesには面白い仕様があります。Xにユニオン型を渡した時の挙動を想像してください。例えば下のように型を受け取ってその型で配列を作成する型に、ユニオン型を引数として渡すことを考えます。

type ToArray<Type> = Type extends any ? Type[] : never
type TypeScript = ToArray<'type' | 'script'>

この時TypeScriptの型はどうなるでしょうか。これから説明する仕様を知らない場合('type' | 'script')[]となると考えられます。しかし、この結果は'type'[] | 'script'[]となります。これはXがユニオンの場合二つに分かれることに起因しています。つまり'type' | 'script' extends any'script' extends any | 'script' extends anyのように別れて評価されます。この仕様をDistributive Conditional Types
と言います。

never

次はneverについて考えていきます。neverがstringとユニオン型を取ることを考えてください。string | neverこれが使用されるときは使用者はstringとして扱います。つまりneverは空(ないもの)として扱われるわけです。そのためneverをユニオン型として扱おうとした時、空として扱われます。

結論

以上のConditional Typesの仕様とneverとユニオンの仕様を組み合わせて考えると、Xがneverだったときこの型は extends Yと分離されて、空との比較になってしまします。そのため、never extends never ? true : false extends never ? true : falseとなってfalseを返すようになってしまうわけです。
そこで、neverを直接比較せず、never[][never]のようにして比較することを考えます。neverをユニオンとして扱うとからですが、never[][never]をユニオンとして扱った場合は配列ですので空になりません。そのため最初に紹介した

type IsNever<X> = X[] extends never[] ? true : false;

が有効となります。察しの通り

type IsNever<X> = [X] extends [never] ? true : false;

でも有効な結果が返ってきます。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0