🔎

TypeScript: Any, Unknown, Never

2024/04/14に公開

普段TypeScriptを使っているけど、Any, Unknown, Neverの型についてちゃんと理解するためにまとめてみた。(英訳はchatGPT)
I usually use TypeScript, but I hadn't fully understood the types Any, Unknown, and Never, so I summarized them.

Any

どんな値(関数を含む)でも代入ができちゃう。
プロパティ、メソッドを使用することもできるが、コンパイルエラーでバグに気付けない。
型定義を放棄した型で、もはや何でも許されてしまう(なにかクイックにテストしたいときは便利)。
tsconfig.jsonで、any型の使用を防ぐオプションとしてnoImplicitAnyがある。

You can assign any value (including functions). You can also use properties and methods, but no compile errors occurs. It's a type where type definitions are abandoned, allowing anything (convenient for quick testing). In tsconfig.json, there's an option noImplicitAny to prevent the use of any type.

let object: any = { x: 0 };
// 以下全てでコンパイルエラーは起きない
// No compile errors occur in any of the following
object.foo();
object();
object.bar = 100;
object = "hello, world!";
const n: number = object;

Unknown

Anyと同様でどんな値でも代入ができるが、代入したオブジェクトのプロパティ、メソッドは使用できないし、実行できない。
意図しないランタイム時のエラーを防止できる。

Similar to Any, you can assign any value, but you can't use or execute properties or methods of the assigned object. It prevents unintended runtime errors.

let object: unknown = { x: 0 };
object.foo();
// 'object' is of type 'unknown'.
object();
// 'object' is of type 'unknown'.

object.bar = 100;
// 'object' is of type 'unknown'.

object = "hello, world!"; // 再代入はOK
const n: number = object;
// Type 'unknown' is not assignable to type 'number'.

Never

値を持たない型。何も代入できない(anyもふくめて)が、neverにneverは代入可能。

A type that has no value. You can't assign anything (including any), but you can assign never to never.

const any: any = 1;
const value1: never = any;
// Type 'any' is not assignable to type 'never'.

const nev = 1 as never; // OK

逆に、何にでも代入はできる。
Conversely, you can assign never to other types.

const a: string = nev; // OK
const b: string[] = nev; // OK

値がない型とはなにか? - What is a type with no value?

例外が必ず発生する関数や無限ループなど終了しない関数などの戻り値は、never型になる。

The return values of functions that always throw exceptions or never-ending functions like infinite loops become type never.

function generateError(message: string, code: number) {
  throw { message: message, errorCode: code };
}

function foreverLoop() {
  while (true) {}
}

Voidとの違い - Difference from Void

戻り値がないという点では同じだが、voidは関数が終了した場合に返される値の一方で、neverは関数の処理が中断されたり永遠に続く場合に返されるもの。

While both indicate no return value, void represents a value returned when a function ends, whereas never represents a value returned when the function's execution is interrupted or continues indefinitely.

何も代入できないことを応用した網羅性チェック - Application of inability to assign anything for Exhaustive Check

neverはnever以外何も代入を許さないので、網羅性チェックに応用できる。
例えば、Union型の分岐処理をするときに、すべてのパターンを網羅しているかをコンパイラにチェックさせることができる。

Since never allows nothing but never to be assigned, it can be applied to exhaustive checking. For example, when branching Union types, you can check if all patterns are covered by the compiler.

type Pattern = "A" | "B" | "C";

class ExhaustiveError extends Error {
  constructor(value: never, message = `Unsupported type: ${value}`) {
    super(message);
  }
}

function printPattern(ext: Pattern): void {
  switch (ext) {
    case "A":
      console.log("Pattern A");
      break;
    case "B":
      console.log("Pattern B");
      break;
    default:
      throw new ExhaustiveError(ext);
      // Argument of type 'string' is not assignable to parameter of type 'never'.
  }
}

改めてTSの基礎を学ぶのも楽しい。

Discussion