TypeScript における型変換のすべて:完全制覇ガイド

TypeScript における型変換のすべて:完全制覇ガイド

D
dongAuthor
2 min read

TypeScript を使っていると、コンパイラが型を正しく推論できなかったり、開発者がより正確な型を把握している場合があります。DHH(David Heinemeier Hansson)が hotwired/turbo プロジェクトで TypeScript を除去し、「TypeScript は本当に必要なのか?」という議論が再燃しました。しかし、大規模プロジェクトにおいて TypeScript の静的型システムがもたらす信頼性は依然として魅力的です。

TypeScript 追放?

このような型システムをさらに柔軟かつ強力に使いこなすためには、「型変換(Type Casting)」または「型断言(Type Assertion)」の理解が不可欠です。本記事では TypeScript のさまざまな型変換手法を見ていき、各状況に応じた最適な方法を選ぶ基準を提示します。

まず、重要な点を押さえておきましょう。TypeScript の型変換はランタイムで値の実際の形を変えるものではありません。あくまで コンパイル時に型チェッカに「この値は私が指定するこの型として扱ってください」と伝える行為 です。ランタイムのコードには何の影響も与えません。

as:もっとも基本的な型断言

as キーワードは TypeScript で最も一般的に使われる型断言構文です。開発者がコンパイラより型をよく知っているとき、その確信のもと型を指定できます。特に anyunknown のように型情報がない値を具体的な型として扱う場合に便利です。

interface Hero {
  name: string;
  age: number;
}

const capt = {} as Hero; // 空オブジェクトを Hero 型として断言
capt.name = 'キャプテン';
capt.age = 100;

ちなみに、JSX(TSX)とともに使うときは、山括弧(<Type>)構文が JSX タグと混同される可能性があるため、as キーワードを使用するのが標準のようになっています。ぜひ覚えておいてください!

as const:値をリテラル型として固定する

as const は変数やオブジェクトをまるで const 宣言したかのように扱い、すべてのプロパティを readonly に変え、値を非常に具体的な リテラル型 に固定します。

// 通常の場合、'direction' の型は 'string' と推論されます。
let direction = "left"; 

// 'as const' を使うと型が "left" というリテラル型に固定されます。
const directionConst = "left" as const;
// directionConst の型: "left"

const config = {
  method: "GET",
  cache: false,
} as const;

/*
config の型: 
{ 
  readonly method: "GET"; 
  readonly cache: false; 
}
*/

このようにすることで、オブジェクトのプロパティが誤って変更されることを防げますし、特定の文字列値のみを許容する関数の引数に渡すときに型の安全性を高めることができます。

!:Non‑null 断言演算子

! 演算子は、ある値が null または undefined では絶対ないとコンパイラに強く主張したいときに使います。DOM 要素にアクセスするときなど、開発者がその要素が必ず存在すると確信している状況で有効です。

// コンパイラは querySelector が null を返す可能性があると警告します。
const el = document.querySelector("input")!; // '!' で null ではないと断言
el.focus(); // これで el が null ではないとみなされ、エラーになりません。

ただしこの方法は慎重に使うべきです。もしランタイムで実際に値が null だった場合、コードはエラーを起こして停止します。!コンパイラの有用な警告を無視する行為 なので、本当に 100%確信できる状況でだけ使うのが良いでしょう。

satisfies:型を守りつつ推論は活かす

ES2022 に導入された satisfies 演算子は、as の欠点を補う非常に賢い手法です。ある値が特定の型を「満たしている (satisfy)」かをチェックしながらも、値そのものの具体的な リテラル型推論はそのまま維持してくれます。

as を使うと型が強制的に固定されて、もとの具体的な型情報を失ってしまうことがあります。しかし satisfies は型チェックを通過させつつ、型推論の利点をすべて享受できるのです。

type Config = { method: "GET" | "POST"; url: string };

// 'as' を使った場合
const apiConfigAs = {
  method: "GET",
  url: "/users",
} as Config;
// apiConfigAs.method の型は "GET" | "POST" に拡張されます。

// 'satisfies' を使った場合
const apiConfigSatisfies = {
  method: "GET",
  url: "/users",
} satisfies Config;
// apiConfigSatisfies.method の型は "GET" リテラル型として維持されます!

もしオブジェクトに Config 型にないプロパティを追加したなら、satisfies はコンパイルエラーを発生させてミスを防ぎます。このように、型チェックと型推論の両方の利点を得たいとき、satisfies は最高の選択と言えるでしょう。

二重断言 (Double Assertion)

関連のない型同士は直接型変換ができません。そんなときに unknown(または any)を中間の橋渡しに使う「二重断言」技法を使うことがあります。

const value: string = "123";

// エラー:string 型を number 型に直接変換できません。
// const num = value as number; 

// 二重断言: string -> unknown -> number
const num = value as unknown as number; // 危険ですが、コンパイラを通過します。

この方法は型チェッカを完全に回避するものなので非常に危険です。コードが実際に何をしているかを正確に理解しており、他に方法がないときの最後の手段としてのみ使うべきです。ほとんどの場合はユーザー定義型ガード(次章で説明)などでより安全に解決できます。

is:ユーザー定義型ガードで型を絞る

条件文を使って型を絞ることを「型ガード (Type Guard)」と言います。typeofinstanceof などが代表的です。ここからさらに進んで、is キーワードを使うことで ユーザー定義型ガード関数 を作ることができます。複雑なオブジェクトの型判別ロジックを関数に切り出し、コードの再利用性と可読性を高めることが可能です。

// 'value is string' は、この関数が true を返したなら value の型が string であることを保証します。
function isString(value: unknown): value is string {
  return typeof value === "string";
}

function print(value: unknown) {
  if (isString(value)) {// このブロック内で value は string 型として安全に扱えます。console.log(value.toUpperCase());
  }
}

このように is を活用すると、複雑な条件付き型ロジックを明確かつ安全に管理することができます。

より良いコードのための提言

TypeScript の型変換は強力なツールですが、乱用すると型システムの利点を損なうことになります。「私はコンパイラより賢い」という自信が必要な場面もありますが、ほとんどの場合コンパイラの警告には理由があります。

型変換を使う前に次のことをまず考えてみてください。

  • 型ガードtypeofinstanceofin またはユーザー定義 is 関数)で型を安全に絞れないか?
  • satisfies 演算子 を使って型チェックと型推論の両方を満たせないか?
  • ジェネリック (Generic) を使ってもっと柔軟な型構造を作れないか?

こうした検討を経た上でもなお型変換が必要なのであれば、その意味と潜在的なリスクを正しく理解して使用してください! 正しく使われた型断言は、あなたの TypeScript コードを一段上のレベルに導いてくれます。

TypeScript を使ううえで、次の二つの格言があります。

  1. any を使うな。
  2. 型断言を使うな。

しかし、本当に確信があるときに型断言を使うことは、コード作成を格段に早めることができますね!

References