【実務・中級編】 JSDocにおけるユニオン型の定義 – JavaScript実践ガイド

お疲れ。最近、TypeScriptへのリプレイス案件が増えてるけどさ、あえて「純粋なJavaScript(Vanilla JS)」で巨大なアプリケーションを組み上げなきゃいけない現場に放り込まれたり、あるいはライブラリのコア部分を書いていて「型安全にしたいけどビルドツールを入れたくない」なんて局面に直面したことはないかい?

そんな時、JSDocはまさに僕たちの「最強の隠し武器」になる。特に、複数の型を受け付ける「ユニオン型」の定義をマスターしておくと、IDE(VS Codeなど)の入力補完や静的解析(TypeScriptのチェッカー)をフル活用できるようになり、泥臭いバグを圧倒的に減らせるんだ。

今日は、JSDocにおけるユニオン型の定義方法について、JavaScriptの裏側の仕様や実務で使えるリアルなテクニックを交えながら、徹底的に解説していこう。

—

なぜJavaScriptでJSDocの「ユニオン型」なのか?

JavaScriptは動的型付け言語だ。変数には何でも入れられるし、関数もどんな引数を受け取っても文法エラーにはならない。
しかし、開発規模が大きくなり、チームメンバーが増えてくると、この「何でも入れられる自由」が「負債」に変わる。

「おい、この関数の第1引数、文字列も入るって聞いてたのに、オブジェクト渡したら落ちたぞ!」
……なんてコードレビューのやり取り、もう終わりにしたいよな。

そこで登場するのがJSDocだ。TypeScriptを導入しなくても、コメントベースで型を定義するだけで、VS CodeがJIT(即時)で型チェックを行い、おかしなコードを書いた瞬間に赤波線を引いて教えてくれる。

特に「この変数は文字列か、あるいはnullかもしれない」といった、複数の型を許容するユニオン型(Union Types)を使いこなせるようになると、JavaScriptの表現力はグッと跳ね上がるんだ。

—

JSDocにおけるユニオン型の基本:パイプ(`|`)記法

JSDocでユニオン型を定義するのは非常にシンプルだ。TypeScriptの型定義とほぼ同じで、複数の型をパイプ記法(`|`)で繋ぐだけ。

百聞は一見に如かず。まずは実務でよくある、IDや名前を受け取る関数のサンプルを見てくれ。

/

  • ユーザーIDまたはユーザー名をもとに、データベースからユーザー情報を取得する
  • @param {string | number} userIdOrName – 検索キー(文字列のユーザー名、または数値のID)
  • @returns {Object|null} 該当するユーザーオブジェクト、見つからない場合はnull

/
function fetchUser(userIdOrName) {
// 内部で型に応じた処理を分岐させる(typeof演算子の出番だね)
if (typeof userIdOrName === ‘number’) {
console.log(`数値ID: ${userIdOrName} で検索します`);
// ID検索のロジック…
} else if (typeof userIdOrName === ‘string’) {
console.log(`ユーザー名: ${userIdOrName} で検索します`);
// 名前検索のロジック…
} else {
throw new TypeError(‘予期せぬ型が渡されました’);
}

// ダミーの戻り値
return { id: 1, name: ‘Taro’ };
}

// 【利用側の例】どちらを渡してもVS Codeは文句を言わない
fetchUser(12345); // 数値を渡す
fetchUser(‘taro_dev’); // 文字列を渡す
// fetchUser(true); // ⚠️ ここでVS Codeが「booleanはアカンでしょ」と黄色い警告を出してくれる!

どうだろう?これだけで、IDEの入力補完と型安全性が手に入る。ビルドプロセスを挟まない素のJS環境において、これほど心強い味方はいない。

—

現場で即戦力になる!高度なユニオン型のパターン

基本を押さえたところで、もう少し実務の現場で泥臭く使われている応用パターンを見ていこう。

1. リテラル型との組み合わせ(特定の文字列しか許さない)

「この設定値には ‘sm’, ‘md’, ‘lg’ のいずれかしか入れさせたくない!」という要件、よくあるよね。JSDocなら文字列リテラルをユニオンで繋ぐことで、擬似的なEnum(列挙型)が作れる。

/

  • ボタンのサイズを変更する
  • @param {HTMLElement} element – 対象のDOM要素
  • @param {‘sm’ | ‘md’ | ‘lg’} size – ボタンのサイズ(指定外の文字列は許容しない)
  • @returns {void}

/
function setButtonSize(element, size) {
// クラスを付与する処理などを想定
element.className = `btn-${size}`;
}

setButtonSize(document.getElementById(‘submit-btn’), ‘md’); // OK
// setButtonSize(btn, ‘xl’); // ⚠️ 警告: ‘xl’ は定義されていません

2. オブジェクトのユニオン型と「ナローイング」

APIからのレスポンスや、ステート管理の都合上、「成功時のオブジェクト」と「エラー時のオブジェクト」のどちらが返ってくるか分からない、というケースはフロントエンド開発の日常茶飯事だ。

ここでJavaScriptの裏側の話をしておくと、JSの実行環境(V8エンジンなど)は、実行時にオブジェクトの構造(Hidden Class / 形状)を見てメモリ上の効率的なアクセスを行っている。僕たち書き手も、コード上で安全に型を絞り込む(ナローイング)必要がある。

/

  • @typedef {Object} SuccessResponse
  • @property {‘success’} status – 成功ステータス(リテラル型)
  • @property {Object} data – 取得したデータ

/

/

  • @typedef {Object} ErrorResponse
  • @property {‘error’} status – エラーすステータス(リテラル型)
  • @property {string} message – エラーメッセージ

/

/

  • APIからのレスポンスをハンドリングする
  • @param {SuccessResponse | ErrorResponse} response – APIレスポンス

/
function handleApiResponse(response) {
// ‘status’ プロパティの値(判別可能なプロパティ / Discriminated Union)で分岐する
if (response.status === ‘success’) {
// このブロック内では、VS Codeは自動的に response を SuccessResponse として解釈する!
console.log(‘データ取得成功:’, response.data);
} else {
// このブロック内では ErrorResponse として解釈される
console.error(‘エラー発生:’, response.message);
}
}

この「判別可能なプロパティ(Discriminated Union)」を使った書き方は、TypeScriptでもJSDocでも共通のベストプラクティスだ。`status` や `type` といった共通のキーを持たせ、そのリテラル値で分岐させることで、ランタイムのエラーを防ぎつつ、IDEの補完を100%引き出すことができる。

—

シニアからの実践的なアドバイスと注意点

最後に、実務でJSDocのユニオン型を運用する上で、僕がチームメンバーによく伝えている注意点をいくつかシェアしておこう。

1. 括弧(`()`)を上手に使って可読性を保つ
複雑な型、例えば「オブジェクトの配列、または数値、あるいはnull」みたいなカオスな型を定義する時は、`(Object|null)[] | number` のように丸括弧を使って優先順位を明確にしよう。コメントとはいえ、コードの可読性は命だ。

2. JSDocコメントが長くなりすぎたら `@typedef` に逃げる
関数のシグネチャ(引数部分)に直接 `{|string|number|Object|null}` なんて書くと、ソースコードが地獄のように汚くなる。複雑なユニオン型は、ファイルの先頭や別ファイルで `@typedef` を使って名前をつけ、それを参照するようにしよう。

/

  • @typedef {string | number | boolean} PrimitiveValue

/

/

  • @param {PrimitiveValue} val

/
function processValue(val) { … }

これだけで、コードの見た目が劇的にスッキリする。

—

まとめ

JSDocのユニオン型は、TypeScriptを導入できない、あるいはしたくないプロジェクトにおいて、開発者体験(DX)を保つための強力な防衛線だ。

「たただのコメント」と侮るなかれ。適切に記述されたJSDocは、現代の賢いIDEと結びつくことで、静的型付け言語に負けない堅牢な開発環境を僕たちにもたらしてくれる。

明日からのコードレビューで、曖昧な “(Any)を書いている後輩がいたら、ぜひこのユニオン型を教えてあげてほしい。チーム全体のコードの品質が、一段階上に引き上げられるはずだ。それじゃ、また現場で会おう!

コメント

タイトルとURLをコピーしました