🌳

おかえり never のもりへ

に公開

こんにちは

型のくにからこんにちは。
最近型レベルプログラミングにハマりつつある yossuli です。
#1 日 1Zenn はちょっと無茶があり期間があいてしまいましたが、前回触れた never の特殊?な挙動についてまとめていきます。

any でも unknown でもない、そのあいだのなめらかなゆめ

never とは型システムを集合論としてとらえたときに「空集合」を表す型です。
空集合であるがゆえに、never には代入することはできず、逆に never は型レベルではどんな型にも代入可能です。

declare const never: never;
declare const any: any;
declare const string: string;

// OK
const a: never = never;
const b: any = never;
const c: string = never;
// Error
const d: never = any;
const e: never = string;

ただし、never 型の値は存在しないため、never は型レベルでどんな型にでも代入可能であることには意味がありません。

おかえり never のもりへ

never が型レベルで空集合であることにどのような意味があるのか、実際のコードを見ていきましょう。

前提知識

まず、never が空集合であることがどのように役立つのかを理解するために、conditional type の挙動について確認しておく必要があります。
一般的な挙動については省略して、extends 句の左辺にユニオン型が来た場合の挙動を見ていきます。

type Conditional<T> = T extends string ? "string" : "other";
// Conditional<string | number>
// => "string" | "other"

このように、extends 句の左辺にユニオン型が来た場合、ユニオンの各要素に対して条件を評価し、結果をユニオンとして返します。
ユニオンとして解釈されたくない場合は [] で両辺を囲むことで回避できます。

ユニオンから特定の型を除外する

type Exclude<T, U> = T extends U ? never : T;
// Exclude<string | number, string>
// => never | number
// => number

// Exclude<string | number | boolean, string | boolean>
// => never | number | never
// => number

このように、特定の型に対して never を返すことで、never が空集合であることを利用して、ユニオン型から特定の型を除外することができます。

マッピング型から特定のプロパティを除外する

type Omit<T, K extends keyof T> = {
  [P in keyof T as P extends K ? never : P]: T[P];
};
// Omit<{ a: string; b: number; c: boolean }, "b">
// => { a: string; never: string; c: boolean }
// => { a: string; c: boolean }

このように、プロパティを never にすることで、never が空集合であることを利用して特定のプロパティを除外することができます。

人はいろいろなものに名前を付けました

あくまで never は型レベルで空集合を表す名前です。
僕のように never に対して存在する型として扱わないように...

さようなら

なんか never って null より null らしいなというところから書き始めた駄文でしたがお付き合いいただきありがとうございました。
null² は素晴らしいパビリオンなので是非訪れてみてください。

Discussion