前置き
JavaScriptは動的型付け言語のため、コード上での型の概念は存在しない。
一方、関数の引数として与えるObjectの中身などを定義したい場合がある。
開発言語としてTypeScriptを採用することで型の定義を容易に行うことができるが、
既にJavaScriptで書かれたコードを改修する場合等、必ずしもTypeScriptが使えない場合も存在する。
そのような場合にJavaScript上で型定義を行う方法を紹介する。
問題例
例として、以下のような関数が与えられた場合について取り上げる。
/**
* 社員の一覧をconditionに従って取得する
* @param {Object} condition 検索する条件
* @return {Array.<Object>} 取得した社員一覧
*/
const fetchMembers = condition => {
/*
* 1000行くらいの処理。至るところでconditionが使われている。
* condition.nameやcondition.idなどが見えるが、
* 何に使われているのか、どんな型の値が入っているのかがわかりにくい。
* しかも、この関数から別の関数へconditionを渡している箇所もある。
*/
}
関数の引数にconditionとしてオブジェクトを与えれば良いのは読み取れるが、
condition変数の中身に何を指定すれば良いのかが全くわからない。
関数を使うためには、既存の呼び出し箇所や関数本体の中身を調査する必要がある。
@typedefを用いた型定義
上記のような問題は、コーディング時に型定義を明記しておくことによりある程度回避が可能である。
@typedefを用いてObjectの中身を明示的に定義することでコードの可読性を上げることができる。
fetchMembers関数が定義されているjsファイルに以下のような記述を追加する。
/**
* @typedef {Object} SearchCondition 社員検索時の条件
* @property {string} name 社員の名前
* @property {string | number} id 社員ID(文字列か数値を入れることができる)
* @property {string} [hoby] 趣味([]で囲むことで、Optional値を指定できる(入れなくても良いパラメータ))
*/
その後、先ほどのfetchMembers関数のドキュメンテーションコメントを以下のように改修することで、
SearchConditionとしてObjectの中身が定義され、
呼び出し側からはどのようなObjectを与えれば良いか読みやすくなる。
/**
* 社員の一覧をconditionに従って取得する
* @param {SearchCondition} condition 検索する条件
* @return {Array.<Object>} 取得した社員一覧
*/
const fetchMembers = condition => {
/*
* 1000行くらいの処理。至るところでconditionが使われている。
* condition.nameやcondition.idなどが見えるが、
* 何に使われているのか、どんな型の値が入っているのかがわかりにくい。
* しかも、この関数から別の関数へconditionを渡している箇所もある。
*/
}
また、@typeを用いることで変数宣言の際に型を指定することもできる。
/** @type {searchcondition} */
const searchcondition = {
name: "hogehoge",
id: 100
};
const members = fetchMembers(searchcondition);
エディタとしてVisualStudioCodeを用いると、以下のようにコード補完を表示することができるようになる。