TypeScriptで関数の戻り値の型を推測して型定義する方法

はいさい！ちゅらデータぬオースティンやいびーん！

# 概要

TypeScriptで通常通り定義した関数の戻り値の型を推測した上で、新しい型として定義する方法を紹介します。

## 背景

TypeScriptを書いていて、<q>この関数の戻り値の型って、他のところでも使いたいんだけど、わざわざ`type`構文で定義するのだるいし</q>と、煩わしく思ったことがありませんでしょうか？

もしくは、戻り値の型と、他の関数の引数の型が微妙に違って、TypeScriptのインタープリターに警告を吐かれたこと、ありませんか？

このような経験を繰り返していると、<q>TypeScript、にりー</q>と思うようになって、TypeScriptの良さが伝わらないのです。

そこで、実は、助けになる`UtilityType`があります。

`ReturnType`なのです。

使い方を説明していきましょう。

# `ReturnType`の使い方

まず最初に、以下のようなコードを例に挙げます。

```typescript
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`の出番

指示を受けた我々は、すぐにそのロジックを書きました。

```typescript
const renderListItems = (listElements: ReturnType<typeof createListItemElements>) => {
    const ul = document.querySelector("ul")!;
    ul.innerHTML = "";
    ul.append(...listElements);
};
```

そうです、かっこよくも`ReturnType`を使うことでなんと **-20秒** の作業時間を短縮できました。

さらに一歩進んで、以下のような型定義をすることができます。

```typescript
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を導入することに及び腰だった場合、<a href="https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html">JSDocs</a>を使ってTypeScriptの機能を活かすことができます。

JSDocsによるTypeScriptは、あくまでも単なるコメントなので、**文句を言われる筋合いはない**、というところが売りです。

上記のコードをJSDocs付きのJavaScriptに書き直すと以下のようになります。

```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プロになっていきます。