はいさい!ちゅらデータぬオースティンやいびーん!
概要
TypeScriptで通常通り定義した関数の戻り値の型を推測した上で、新しい型として定義する方法を紹介します。
背景
TypeScriptを書いていて、この関数の戻り値の型って、他のところでも使いたいんだけど、わざわざ
と、煩わしく思ったことがありませんでしょうか?type
構文で定義するのだるいし
もしくは、戻り値の型と、他の関数の引数の型が微妙に違って、TypeScriptのインタープリターに警告を吐かれたこと、ありませんか?
このような経験を繰り返していると、TypeScript、にりー
と思うようになって、TypeScriptの良さが伝わらないのです。
そこで、実は、助けになるUtilityType
があります。
ReturnType
なのです。
使い方を説明していきましょう。
ReturnType
の使い方
まず最初に、以下のようなコードを例に挙げます。
const theShining = ["RedRum", "All work and no play makes Jack a dull boy"];
const createListItemElements = (contents: string[]) => contents.map(content => {
const li = document.createElement("li");
li.classList.add("come-play-with-us-danny");
li.textContent = content;
li.addEventListener("click", () => alert(`List item ${content} was clicked!`), { once: true });
return li;
});
我々は開発者として、文字列が入っている配列を<li class="come-play-with-us-danny">
要素の配列に変換する関数を作るように指示を受けました。
また、その<li>
の配列の各要素は、クリックすると、一回だけ、alert
を出すようにするのだと。
その仕事が終わって、上司にこう言われました。
なんだか不気味な実装だが、よかろう。
<li>の配列をDOMにレンダーできるようにせぇ!
ReturnType
の出番
指示を受けた我々は、すぐにそのロジックを書きました。
const renderListItems = (listElements: ReturnType<typeof createListItemElements>) => {
const ul = document.querySelector("ul")!;
ul.innerHTML = "";
ul.append(...listElements);
};
そうです、かっこよくもReturnType
を使うことでなんと -20秒 の作業時間を短縮できました。
さらに一歩進んで、以下のような型定義をすることができます。
type ScaryListElements = ReturnType<typeof createListItemElements>;
const addAnotherEventListener = (listElements: ScaryListElements) =>
listElements.forEach(li => li.addEventListener("transitionend", () => console.log("Transition Ended.")));
const renderListItems = (listElements: ScaryListElements) => {
const ul = document.querySelector("ul")!;
ul.innerHTML = "";
ul.append(...listElements);
};
普通にconst renderListItems = (listElements: HTMLLIElement[]) => {
と書くことももちろんできました(その方もおそらく早かったでしょう)が、こうして、複数の他の関数等で使うことになったり、戻り値の型がややこしかったりすると、やはり、このUtilityType
が優れた選手として活躍してくれます。
JavaScript環境で開発している時に、JSDocsでもできます。
TypeScriptを使うメンバーがいなかったり、プロジェクト全体でTypeScriptを導入することに及び腰だった場合、JSDocsを使ってTypeScriptの機能を活かすことができます。
JSDocsによるTypeScriptは、あくまでも単なるコメントなので、文句を言われる筋合いはない、というところが売りです。
上記のコードをJSDocs付きのJavaScriptに書き直すと以下のようになります。
//@ts-check
/** @typedef {ReturnType<typeof createListItemElements>} ScaryListElements */
const theShining = ["RedRum", "All work and no play makes Jack a dull boy"];
/** @param {string[]} contents */
const createListItemElements = (contents) => contents.map(content => {
const li = document.createElement("li");
li.classList.add("come-play-with-us-danny");
li.textContent = content;
li.addEventListener("click", () => alert(`List item ${content} was clicked!`), { once: true });
return li;
});
/** @param {ScaryListElements} listElements */
const addAnotherEventListener = (listElements) =>
listElements.forEach(li => li.addEventListener("transitionend", () => console.log("Transition Ended.")));
/** @param {ScaryListElements} listElements */
const renderListItems = (listElements) => {
const ul = /** @type {HTMLUListElement!} */ (document.querySelector("ul"));
ul.innerHTML = "";
ul.append(...listElements);
};
こうするだけで上記のTypeScriptと同じようなIntellisenseと型チェックができるので、JSDocsは最高ですよ!
まとめ
ここまで、ReturnType
の使い方を説明してまいりましたが、いかがでしょうか?
TypeScriptは使えば使うほど、苦手意識がなくなって、好きになってくるものです。
こういう便利ツールを駆使していくと、どんどんTSプロになっていきます。