#記念すべき第一回、型定義について
##JavaScriptの型定義(仮)の方法
TypeScriptはJavaScriptに型定義その他を加えたものだが、素のJavaScriptでも型定義もどきを行うことができると最近知ったので共有しようと思う。
因みにエディタはcursorを使っていて、記事の内容に関してもまんべんなくAIを使用していくのでそのつもりでよんでほしい。自分で全部書くよりAIに任せたほうが読むほうも安心なのではなかろうか
これは@paramという書き方らしい。
@paramとは
@paramはJSDocというJavaScriptのドキュメント生成ツールで使用されるタグで、関数のパラメータ(引数)の説明を記述するために使われます。
基本的な@paramの記法
/**
* ユーザー情報を表示する関数
* @param {string} name - ユーザーの名前
* @param {number} age - ユーザーの年齢
* @param {string} [email] - ユーザーのメールアドレス(オプション)
* @returns {void}
*/
function displayUserInfo(name, age, email) {
console.log(`名前: ${name}`);
console.log(`年齢: ${age}`);
if (email) {
console.log(`メール: ${email}`);
}
}
// 使用例
displayUserInfo("田中太郎", 25);
displayUserInfo("佐藤花子", 30, "hanako@example.com");
様々な@paramの使い方
1. 基本的な型指定
/**
* 数値計算を行う関数
* @param {number} a - 1つ目の数値
* @param {number} b - 2つ目の数値
* @param {string} operation - 演算子('+', '-', '*', '/')
* @returns {number} 計算結果
*/
function calculate(a, b, operation) {
switch (operation) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return b !== 0 ? a / b : NaN;
default: throw new Error('無効な演算子です');
}
}
console.log(calculate(10, 5, '+')); // 15
console.log(calculate(10, 5, '*')); // 50
2. オプショナルパラメータ
/**
* ユーザーを作成する関数
* @param {string} name - ユーザー名(必須)
* @param {number} [age] - 年齢(オプション)
* @param {string} [email] - メールアドレス(オプション)
* @returns {Object} ユーザーオブジェクト
*/
function createUser(name, age, email) {
const user = { name };
if (age !== undefined) {
user.age = age;
}
if (email !== undefined) {
user.email = email;
}
return user;
}
// 使用例
const user1 = createUser("田中太郎");
const user2 = createUser("佐藤花子", 25);
const user3 = createUser("山田次郎", 30, "yamada@example.com");
console.log(user1); // { name: "田中太郎" }
console.log(user2); // { name: "佐藤花子", age: 25 }
console.log(user3); // { name: "山田次郎", age: 30, email: "yamada@example.com" }
3. 複数の型を許可
/**
* IDを検証する関数
* @param {string|number} id - 文字列または数値のID
* @param {Array<string>} validTypes - 有効なIDタイプの配列
* @returns {boolean} 有効なIDかどうか
*/
function validateId(id, validTypes) {
if (typeof id === 'string') {
return validTypes.includes('string') && id.length > 0;
} else if (typeof id === 'number') {
return validTypes.includes('number') && id > 0;
}
return false;
}
console.log(validateId("user123", ["string", "number"])); // true
console.log(validateId(42, ["string", "number"])); // true
console.log(validateId("", ["string", "number"])); // false
4. オブジェクトと配列の型指定
/**
* ユーザーリストを処理する関数
* @param {Array<Object>} users - ユーザーオブジェクトの配列
* @param {Object} filterOptions - フィルターオプション
* @param {string} [filterOptions.name] - 名前でフィルター
* @param {number} [filterOptions.minAge] - 最小年齢
* @returns {Array<Object>} フィルターされたユーザーリスト
*/
function filterUsers(users, filterOptions) {
return users.filter(user => {
if (filterOptions.name && !user.name.includes(filterOptions.name)) {
return false;
}
if (filterOptions.minAge && user.age < filterOptions.minAge) {
return false;
}
return true;
});
}
const users = [
{ name: "田中太郎", age: 25 },
{ name: "佐藤花子", age: 30 },
{ name: "山田次郎", age: 20 }
];
const filtered = filterUsers(users, { name: "田", minAge: 22 });
console.log(filtered); // [{ name: "田中太郎", age: 25 }]
@paramの利点
- 型安全性: 実行時ではなく、開発時に型エラーを発見できる
- 可読性: 関数の使用方法が明確になる
- IDEサポート: 多くのエディタで入力補完や型チェックが効く
- ドキュメント生成: 自動的にAPIドキュメントを生成できる
まとめ
@paramを使うことで、TypeScriptを使わなくてもJavaScriptで型定義のような効果を得ることができます。特に、チーム開発やライブラリ開発では、コードの可読性と保守性を大幅に向上させることができます。
🔍 新たな発見
とのことらしい。ちなみに@param以外にも@returnというのがあるらしい。
@paramと@returnの違い
@param(パラメータ)
- 関数の入力(引数)を説明
- 関数に渡される値の型と説明を記述
@return(戻り値)
- 関数の出力(戻り値)を説明
- 関数が返す値の型と説明を記述
両方を使った完全な例
/**
* ユーザーの年齢を計算する関数
* @param {number} birthYear - 生年
* @param {number} currentYear - 現在の年
* @returns {number} 計算された年齢
*/
function calculateAge(birthYear, currentYear) {
return currentYear - birthYear;
}
// 使用例
const age = calculateAge(1995, 2024);
console.log(age); // 29
@returnの詳細な使い方
1. 基本的な戻り値の型指定
/**
* 文字列を大文字に変換する関数
* @param {string} text - 変換する文字列
* @returns {string} 大文字に変換された文字列
*/
function toUpperCase(text) {
return text.toUpperCase();
}
console.log(toUpperCase("hello")); // "HELLO"
2. 複数の戻り値パターン
/**
* 数値を検証して結果を返す関数
* @param {number} value - 検証する数値
* @returns {Object} 検証結果オブジェクト
* @returns {boolean} 検証結果オブジェクト.isValid - 有効かどうか
* @returns {string} 検証結果オブジェクト.message - 結果メッセージ
*/
function validateNumber(value) {
if (value > 0) {
return {
isValid: true,
message: "正の数です"
};
} else {
return {
isValid: false,
message: "正の数ではありません"
};
}
}
const result = validateNumber(42);
console.log(result.isValid); // true
console.log(result.message); // "正の数です"
3. 配列やオブジェクトの戻り値
/**
* ユーザーリストをフィルターする関数
* @param {Array<Object>} users - ユーザー配列
* @param {number} minAge - 最小年齢
* @returns {Array<Object>} フィルターされたユーザー配列
*/
function filterUsersByAge(users, minAge) {
return users.filter(user => user.age >= minAge);
}
const users = [
{ name: "田中太郎", age: 25 },
{ name: "佐藤花子", age: 30 },
{ name: "山田次郎", age: 20 }
];
const adults = filterUsersByAge(users, 25);
console.log(adults); // [{ name: "田中太郎", age: 25 }, { name: "佐藤花子", age: 30 }]
4. 戻り値がない場合(void)
/**
* ログを出力する関数
* @param {string} message - 出力するメッセージ
* @returns {void} 戻り値なし
*/
function logMessage(message) {
console.log(`[${new Date().toISOString()}] ${message}`);
}
logMessage("アプリケーションが起動しました");
// 出力: [2024-01-15T10:30:00.000Z] アプリケーションが起動しました
@paramと@returnの組み合わせ例
/**
* 完全なユーザー管理システム
* @param {string} name - ユーザー名
* @param {number} age - 年齢
* @param {string} [email] - メールアドレス(オプション)
* @returns {Object} 作成されたユーザーオブジェクト
* @returns {string} 作成されたユーザーオブジェクト.id - ユーザーID
* @returns {string} 作成されたユーザーオブジェクト.name - ユーザー名
* @returns {number} 作成されたユーザーオブジェクト.age - 年齢
* @returns {string|undefined} 作成されたユーザーオブジェクト.email - メールアドレス
*/
function createUser(name, age, email) {
const userId = `user_${Date.now()}`;
const user = {
id: userId,
name: name,
age: age
};
if (email) {
user.email = email;
}
return user;
}
// 使用例
const newUser = createUser("鈴木一郎", 28, "suzuki@example.com");
console.log(newUser);
// 出力例: { id: "user_1705312200000", name: "鈴木一郎", age: 28, email: "suzuki@example.com" }
まとめ
- @param: 関数の入力(引数)を説明
- @return: 関数の出力(戻り値)を説明
- 両方を使うことで、関数の完全な仕様を記述できる
- 特に複雑な戻り値の場合は、詳細な説明が重要
💡 重要なポイント
こういうのもあるらしい。ちなみに@~は開発時のみ型定義してくれるもので、コメントアウトしてあるため出力時には効果をなさないようだ。最後にこの解説も載せておく。
その他のJSDocタグ
JSDocには他にも多くの@タグがあります。それぞれの用途と使い方を紹介します。
@throws(例外)
関数が投げる可能性のある例外を説明します。
/**
* 数値を安全に割り算する関数
* @param {number} a - 被除数
* @param {number} b - 除数
* @returns {number} 割り算の結果
* @throws {Error} 0で割り算しようとした場合
*/
function safeDivide(a, b) {
if (b === 0) {
throw new Error("0で割り算することはできません");
}
return a / b;
}
try {
const result = safeDivide(10, 0);
} catch (error) {
console.log(error.message); // "0で割り算することはできません"
}
@example(使用例)
関数の使用例を示します。
/**
* 配列の合計を計算する関数
* @param {Array<number>} numbers - 数値の配列
* @returns {number} 合計値
* @example
* // 基本的な使用例
* const sum = calculateSum([1, 2, 3, 4, 5]);
* console.log(sum); // 15
*
* // 空配列の場合
* const emptySum = calculateSum([]);
* console.log(emptySum); // 0
*/
function calculateSum(numbers) {
return numbers.reduce((sum, num) => sum + num, 0);
}
@deprecated(非推奨)
非推奨の関数やメソッドを明示します。
/**
* 古い計算方法(非推奨)
* @param {number} a - 1つ目の数値
* @param {number} b - 2つ目の数値
* @returns {number} 計算結果
* @deprecated 代わりに {@link calculate} を使用してください
*/
function oldCalculate(a, b) {
return a + b;
}
/**
* 新しい計算方法
* @param {number} a - 1つ目の数値
* @param {number} b - 2つ目の数値
* @returns {number} 計算結果
*/
function calculate(a, b) {
return a + b;
}
// 使用例
// oldCalculate(5, 3); // 非推奨の警告が表示される
calculate(5, 3); // 推奨される方法
@typedef(型定義)
カスタム型を定義します。
/**
* ユーザー情報の型定義
* @typedef {Object} User
* @property {string} id - ユーザーID
* @property {string} name - ユーザー名
* @property {number} age - 年齢
* @property {string} [email] - メールアドレス(オプション)
*/
/**
* ユーザーを作成する関数
* @param {string} name - ユーザー名
* @param {number} age - 年齢
* @param {string} [email] - メールアドレス
* @returns {User} 作成されたユーザー
*/
function createUser(name, age, email) {
return {
id: `user_${Date.now()}`,
name,
age,
email
};
}
@callback(コールバック関数)
コールバック関数の型を定義します。
/**
* 数値処理のコールバック関数
* @callback NumberProcessor
* @param {number} value - 処理する数値
* @returns {number} 処理結果
*/
/**
* 配列の各要素に処理を適用する関数
* @param {Array<number>} numbers - 数値の配列
* @param {NumberProcessor} processor - 処理関数
* @returns {Array<number>} 処理された配列
*/
function processNumbers(numbers, processor) {
return numbers.map(processor);
}
// 使用例
const numbers = [1, 2, 3, 4, 5];
const doubled = processNumbers(numbers, (num) => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
@since(バージョン情報)
機能が追加されたバージョンを記録します。
/**
* 新しい文字列処理機能
* @param {string} text - 処理する文字列
* @returns {string} 処理結果
* @since 2.0.0
*/
function newStringProcessor(text) {
return text.trim().toLowerCase();
}
@author(作者情報)
コードの作者を記録します。
/**
* 高度な数学計算ライブラリ
* @author 田中太郎 <tanaka@example.com>
* @version 1.0.0
* @since 2024-01-15
*/
class MathLibrary {
/**
* 円の面積を計算
* @param {number} radius - 半径
* @returns {number} 面積
*/
static calculateCircleArea(radius) {
return Math.PI * radius * radius;
}
}
@link(関連リンク)
関連する関数やクラスへのリンクを作成します。
/**
* ユーザー認証を行う関数
* @param {string} username - ユーザー名
* @param {string} password - パスワード
* @returns {boolean} 認証成功かどうか
* @see {@link createUser} ユーザー作成
* @see {@link updateUser} ユーザー更新
*/
function authenticateUser(username, password) {
// 認証ロジック
return true;
}
よく使う@タグのまとめ
| タグ | 用途 | 例 |
|---|---|---|
@param |
パラメータの説明 | @param {string} name - ユーザー名 |
@returns |
戻り値の説明 | @returns {number} 計算結果 |
@throws |
例外の説明 | @throws {Error} エラーメッセージ |
@example |
使用例 | @example |
@deprecated |
非推奨の明示 | @deprecated 代わりに... |
@typedef |
型定義 | @typedef {Object} User |
@callback |
コールバック関数 | @callback NumberProcessor |
@since |
バージョン情報 | @since 2.0.0 |
@author |
作者情報 | @author 田中太郎 |
@link |
関連リンク | @see {@link functionName} |
実際の開発での活用例
/**
* 完全なドキュメント付き関数
* @author 開発者名
* @version 1.0.0
* @since 2024-01-15
* @param {string} input - 入力文字列
* @param {Object} options - オプション設定
* @param {boolean} [options.uppercase=false] - 大文字変換するか
* @returns {string} 処理された文字列
* @throws {Error} 無効な入力の場合
* @example
* const result = processText("hello", { uppercase: true });
* console.log(result); // "HELLO"
* @deprecated 代わりに {@link newProcessText} を使用
* @see {@link newProcessText} 新しい処理関数
*/
なんかいっぱいあるみたい。こんなの知らなかった。
TypeScriptを使う場合のJSDocの必要性
TypeScriptだけでも十分な場合
- 基本的な型定義はTypeScriptで完結
- コンパイル時の型チェックが効く
- インターフェースや型エイリアスで型を定義
JSDocも併用する価値がある場合
1. 詳細なドキュメント生成
TypeScriptの型情報だけでは、関数の詳細な説明や使用例が不足することがあります。
// TypeScriptだけの場合
interface User {
name: string;
age: number;
email?: string;
}
function createUser(name: string, age: number, email?: string): User {
return { name, age, email };
}
// JSDoc + TypeScriptの場合
/**
* ユーザーを作成する関数
* @param name - ユーザー名(2文字以上、50文字以下)
* @param age - 年齢(0歳以上、150歳以下)
* @param email - メールアドレス(オプション、有効な形式である必要)
* @returns 作成されたユーザーオブジェクト
* @throws {Error} 名前が無効な場合
* @example
* // 基本的な使用例
* const user = createUser("田中太郎", 25);
*
* // メール付きで作成
* const userWithEmail = createUser("佐藤花子", 30, "sato@example.com");
*
* // エラー例
* try {
* createUser("A", 25); // エラー: 名前が短すぎます
* } catch (error) {
* console.log(error.message);
* }
*/
function createUser(name: string, age: number, email?: string): User {
if (name.length < 2 || name.length > 50) {
throw new Error("名前は2文字以上、50文字以下である必要があります");
}
if (age < 0 || age > 150) {
throw new Error("年齢は0歳以上、150歳以下である必要があります");
}
if (email && !isValidEmail(email)) {
throw new Error("無効なメールアドレスです");
}
return { name, age, email };
}
function isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
2. APIドキュメントの自動生成
JSDocを使うと、HTMLやPDFのドキュメントを自動生成できます。
/**
* ユーザー管理システムのメインクラス
* @class UserManager
* @description ユーザーの作成、更新、削除、検索を行うクラス
* @author 開発チーム
* @version 2.0.0
* @since 2024-01-15
*/
class UserManager {
private users: User[] = [];
/**
* ユーザーを追加する
* @param user - 追加するユーザー
* @returns 追加されたユーザーのID
* @throws {Error} ユーザー名が重複している場合
*/
addUser(user: User): string {
if (this.users.some(u => u.name === user.name)) {
throw new Error(`ユーザー名 "${user.name}" は既に存在します`);
}
const id = `user_${Date.now()}`;
this.users.push({ ...user, id });
return id;
}
/**
* ユーザーを検索する
* @param query - 検索クエリ
* @param options - 検索オプション
* @param options.caseSensitive - 大文字小文字を区別するか(デフォルト: false)
* @param options.limit - 最大結果数(デフォルト: 10)
* @returns 検索結果のユーザー配列
*/
searchUsers(
query: string,
options: { caseSensitive?: boolean; limit?: number } = {}
): User[] {
const { caseSensitive = false, limit = 10 } = options;
let searchQuery = query;
if (!caseSensitive) {
searchQuery = query.toLowerCase();
}
const results = this.users.filter(user => {
const userName = caseSensitive ? user.name : user.name.toLowerCase();
return userName.includes(searchQuery);
});
return results.slice(0, limit);
}
}
3. IDEでの高度なサポート
多くのIDEでは、JSDocコメントから詳細な情報を表示します。
/**
* 複雑な設定オブジェクトの型定義
* @typedef {Object} AppConfig
* @property {string} appName - アプリケーション名
* @property {number} version - バージョン番号
* @property {Object} database - データベース設定
* @property {string} database.host - データベースホスト
* @property {number} database.port - データベースポート
* @property {string} database.name - データベース名
* @property {Object} features - 機能設定
* @property {boolean} features.debug - デバッグモード
* @property {boolean} features.analytics - アナリティクス有効化
*/
/**
* アプリケーション設定を読み込む
* @param {AppConfig} config - アプリケーション設定
* @returns {Promise<boolean>} 設定の読み込み成功かどうか
*/
async function loadAppConfig(config: any): Promise<boolean> {
try {
// 設定の検証
if (!config.appName || !config.database?.host) {
throw new Error("必須設定が不足しています");
}
// 設定の適用
console.log(`アプリケーション "${config.appName}" を起動中...`);
console.log(`データベース: ${config.database.host}:${config.database.port}`);
return true;
} catch (error) {
console.error("設定の読み込みに失敗しました:", error.message);
return false;
}
}
実際の開発での使い分け
TypeScriptだけを使う場合
// シンプルな型定義
interface Point {
x: number;
y: number;
}
function calculateDistance(p1: Point, p2: Point): number {
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
return Math.sqrt(dx * dx + dy * dy);
}
JSDoc + TypeScriptを使う場合
/**
* 2点間の距離を計算する
* @param p1 - 1つ目の点
* @param p2 - 2つ目の点
* @returns 2点間のユークリッド距離
* @example
* const p1 = { x: 0, y: 0 };
* const p2 = { x: 3, y: 4 };
* const distance = calculateDistance(p1, p2); // 5
*/
function calculateDistance(p1: Point, p2: Point): number {
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
return Math.sqrt(dx * dx + dy * dy);
}
まとめ
- TypeScriptだけ: 基本的な開発には十分
- JSDoc + TypeScript:
- 詳細なドキュメントが必要な場合
- チーム開発で可読性を重視する場合
- APIドキュメントを自動生成したい場合
- IDEでの高度なサポートが必要な場合
結論: 個人の小規模プロジェクトならTypeScriptだけでも十分ですが、チーム開発やライブラリ開発ではJSDocを併用する価値が高いです!
🤯 筆者の驚き
こんな感じらしい。「らしい」ばっかりで申し訳ない。ここまで見て超初心者な疑問が浮かんだ。JSDocってなに?と。JavaScriptの何かかな?
JSDocってなに?
JSDocとは
JSDocは「JavaScript Documentation」の略で、JavaScriptのコードにコメントを書くための標準的な書き方のことです。
簡単に言うと
- JavaScriptのコードに特別なコメントを書く
- そのコメントから自動的にドキュメントを作る
- 型情報も一緒に書ける
- コメントなのに開発ツールが理解してくれる
JSDocの基本
普通のコメント vs JSDocコメント
普通のコメント(開発ツールは理解できない)
// ユーザーを作成する関数
// name: ユーザー名
// age: 年齢
// 戻り値: ユーザーオブジェクト
function createUser(name, age) {
return { name, age };
}
JSDocコメント(開発ツールが理解できる)
/**
* ユーザーを作成する関数
* @param {string} name - ユーザー名
* @param {number} age - 年齢
* @returns {Object} ユーザーオブジェクト
*/
function createUser(name, age) {
return { name, age };
}
JSDocが生まれた理由
JavaScriptの歴史
- 昔のJavaScript: 型がなかった
- 問題: 関数の使い方が分からない
- 解決策: コメントで型を書こう!
- 結果: JSDocが生まれた
なぜコメントで型を書くの?
// 昔のJavaScript(型がない)
function processData(input) {
// inputって何?文字列?数値?配列?
// 戻り値は何?分からない...
return input.length;
}
// JSDocで型を明記
/**
* データを処理する関数
* @param {string|Array} input - 文字列または配列
* @returns {number} 文字列の長さまたは配列の要素数
*/
function processData(input) {
return input.length;
}
JSDocの書き方
基本ルール
/**で始める*で各行を始める*/で終わる@で始まる特別なタグを使う
基本的なタグ
/**
* 関数の説明
* @param {型} パラメータ名 - 説明
* @returns {型} 戻り値の説明
*/
実際の例で理解しよう
例1: シンプルな関数
/**
* 2つの数を足す
* @param {number} a - 1つ目の数
* @param {number} b - 2つ目の数
* @returns {number} 足し算の結果
*/
function add(a, b) {
return a + b;
}
// 使用例
const result = add(5, 3); // 8
例2: オブジェクトを返す関数
/**
* ユーザー情報を作成
* @param {string} name - 名前
* @param {number} age - 年齢
* @returns {Object} ユーザー情報
* @returns {string} ユーザー情報.name - 名前
* @returns {number} ユーザー情報.age - 年齢
*/
function createUserInfo(name, age) {
return {
name: name,
age: age
};
}
// 使用例
const user = createUserInfo("田中太郎", 25);
console.log(user.name); // "田中太郎"
console.log(user.age); // 25
JSDocのメリット
1. 開発ツールが賢くなる
- 入力補完が効く
- 型エラーを事前に発見
- 関数の説明が表示される
2. チーム開発が楽になる
- コードの意味が分かりやすい
- 新しいメンバーが理解しやすい
- ドキュメントが自動生成される
3. バグが減る
- 型の間違いを事前に発見
- 関数の使い方が明確
- エラーの原因が分かりやすい
JSDocのデメリット
1. コードが長くなる
// 短い関数でも長いコメントが必要
/**
* 数値を2倍にする
* @param {number} num - 数値
* @returns {number} 2倍の値
*/
function double(num) {
return num * 2;
}
2. コメントを書く手間
- 時間がかかる
- 面倒くさい
- 忘れがち
いつ使うべき?
使うべき場合
- チーム開発
- ライブラリ開発
- 長期間保守するコード
- 複雑な関数
使わなくてもいい場合
- 個人の簡単なスクリプト
- 一度だけ使うコード
- 学習用のコード
まとめ
JSDocとは
- JavaScriptのコードに特別なコメントを書く方法
- 型情報や説明をコメントで書ける
- 開発ツールが理解してくれる
- 自動ドキュメント生成ができる
簡単に言うと
- コメントなのに開発ツールが賢くなる
- コードが分かりやすくなる
- チーム開発が楽になる
初心者へのアドバイス
- 最初は基本的な
@paramと@returnsだけ使う - 慣れてきたら他のタグも使ってみる
- 完璧に書こうとしなくても大丈夫
💭 筆者の理解
要はエディタが認識してくれるコメントということか。JavaScript DocumentでJSDocらしい。
@タグは開発時のみ効く
重要なポイント
@で始まるJSDocタグは開発時のみ効くもので、実行時には完全に無視されます。
なぜ開発時のみ効くのか?
1. コメントは実行されない
/**
* この部分は全てコメントなので、実行されません
* @param {string} name - ユーザー名
* @returns {string} 挨拶メッセージ
*/
function greet(name) {
// 実際に実行されるのはこの部分だけ
return `こんにちは、${name}さん!`;
}
// 実行時には以下と同じ
function greet(name) {
return `こんにちは、${name}さん!`;
}
2. 開発ツールが事前に解析
- エディタ(VS Code、Cursor等)がJSDocを読み取る
- TypeScriptコンパイラが型情報として認識
- Linterがコードの品質チェックに使用
- 実行時には完全に無視される
開発時 vs 実行時の違い
開発時(エディタ上)
/**
* ユーザー情報を取得する関数
* @param {string} userId - ユーザーID
* @returns {Object} ユーザー情報
* @throws {Error} ユーザーが見つからない場合
*/
function getUserInfo(userId) {
// エディタでは以下が表示される:
// - パラメータの型: string
// - 戻り値の型: Object
// - 投げる可能性のある例外: Error
// - 関数の説明
}
エディタでの表示例:
- 入力補完:
getUserInfo(と入力すると、userId: stringと表示 - 型チェック: 間違った型を渡すと警告
- ホバー情報: 関数にマウスを乗せると詳細情報を表示
実行時(ブラウザ/Node.js)
// 実行時には以下と同じ
function getUserInfo(userId) {
// JSDocコメントは完全に無視される
// 型チェックも行われない
// エラーも発生しない
}
実際の動作確認
開発時(エディタ)
/**
* 数値を2倍にする関数
* @param {number} num - 数値
* @returns {number} 2倍の値
*/
function double(num) {
return num * 2;
}
// エディタでは以下が効く:
// ✅ 型チェック: double("文字列") → 警告
// ✅ 入力補完: double( と入力すると num: number と表示
// ✅ ホバー情報: 関数の説明が表示される
実行時(ブラウザ)
// 実行時には以下と同じ
function double(num) {
return num * 2;
}
// 実行時には以下が起こる:
// ❌ 型チェック: double("文字列") → 実行される("文字列文字列")
// ❌ 入力補完: 効かない
// ❌ ホバー情報: 表示されない
なぜこの仕組みなのか?
1. パフォーマンス
- 実行時にコメントを解析すると遅くなる
- 本番環境では不要な処理
2. セキュリティ
- コメントに機密情報が含まれる可能性
- 実行時にコメントを読み取るのは危険
3. 用途の違い
- 開発時: コードの理解、品質向上
- 実行時: 実際の処理、パフォーマンス
実際の開発フロー
1. コーディング時
/**
* ユーザー認証を行う関数
* @param {string} username - ユーザー名
* @param {string} password - パスワード
* @returns {Promise<boolean>} 認証成功かどうか
* @throws {Error} 認証失敗時
*/
async function authenticate(username, password) {
// エディタで型チェック、入力補完が効く
}
2. ビルド時
- TypeScriptコンパイラがJSDocを解析
- 型情報として認識
- コンパイルエラーを検出
3. 実行時
- JSDocコメントは完全に無視
- 純粋なJavaScriptとして実行
- パフォーマンスに影響なし
まとめ
@タグの特徴
- ✅ 開発時: エディタ、TypeScript、Linterが認識
- ❌ 実行時: 完全に無視される
- 🔄 ビルド時: 型チェック、コンパイルに使用
つまり
- JSDocは開発者向けのツール
- 実行時の動作には一切影響しない
- 開発効率とコード品質を向上させる
🎯 今回のまとめ
ということで、今回はJavaScriptの型定義について紹介した。最後まで読んでくれてどうもありがとう。また気になる内容や覚えておきたいこと、書き記しておきたいことを記事にしていくので良ければ見ていってほしい。
💬 筆者の一言
このブログサイトも一応自分で作成したものだが、これに関してはほぼAIに頼ったのでまだまだ高度なことはできない。これから勉強を重ねていきたいと思う。ではまた。