696
377

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

TypeScriptを使って嬉しかったこと

Last updated at Posted at 2020-01-12

新人「先輩、TypeScriptのコーディングできました!」

先輩社員「どれどれ」
先輩社員「いやそこら中コンパイルエラーだらけ...なのは型システムが働いてる証拠だ!」
先輩社員「そうだろ?」

先輩社員「型は...ガードレールだ」
先輩社員「進むべきじゃない場所へ進もうとしたら、ちゃんとブロックしてくれる...」
(ぺこぱ風)

ってことで、静的型付言語って良いですよね。
今回はTypeScriptを使ってみて嬉しかったことを書いてみます。

登場人物

ワイ・・・ワイ(36歳)
社長・・・社長
ハスケル子・・・インターンの中学2年生

今日から新しいプロジェクト開始

社長「おーい、やめ太郎、ハスケル子ちゃん」
社長「新しいお仕事を獲得してきたで」
社長「技術記事投稿サイトを作る案件や」

ワイ「おお〜、さすが社長はんや」

社長「おおきにやで」
社長「ほんで、そのお仕事の話なんやけど」
社長「クライアントはんから一つ要望があんねん」
社長「Reactを使って作ってくれ、って言われてんねや」
社長「やめ太郎、ハスケル子ちゃん、Reactはイケるか?」

ワイ「やった事なくはないですわ」
ハスケル子「私も大丈夫です」

TypeScriptはどうする?

社長「TypeScriptはどうしよか?」
社長「そこはクライアントはんからは何も言われてへんねやけど」

ワイ「TSはよう分からんから、今回はやめときまひょ」
ワイ「型とかよう読まれへんし」

ハスケル子「私は絶対TypeScript有りでやりたいです」
ハスケル子「型がないとコード読むの面倒なので...」

型がないと面倒、とは

ワイ「いや、型があるほうが型情報を読まなあかんから面倒やん」

ハスケル子「いえ、型が書いてあるほうがコードを把握しやすいです」

ワイ「ええ...何で...?」

例えば:マイページ

ハスケル子「例えば、技術投稿サイトのマイページみたいなのを作るとしますね」

スクリーンショット 2020-01-12 20.44.38.png

ハスケル子「↑こんな感じのやつです」

ワイ「ほうほう」

ハスケル子「で、ユーザ情報を表示するためのUserInfoっていうコンポーネントを作るとします」
ハスケル子「TypeScriptなしで書くとすると、コードは↓こんな感じです」

/components/UserInfo.jsx
const UserInfo = ({ user }) => (
  <div>
    <section>
      <h2>ユーザ情報</h2>
      <p>アカウント名:{user.account}</p>
      <p>名前:{user.name}</p>
    </section>
    <section>
      <h2>記事一覧</h2>
      <ol>
        {
          user.articles.map(article => (
            <li>
              <a href={article.url}>{article.title}</a>
            </li>
          ))
        }
      </ol>
    </section>
  </div>
)

ワイ「なるほどな」

ハスケル子「やめ太郎さんなら、このコンポーネントのコードを見て、」
ハスケル子「どんな風に使うべきコンポーネントなのか読み取れますか?」

ワイ「そら簡単やで」
ワイ「余裕で読み取れますがな
ワイ「まず...」

/components/UserInfo.jsx
const UserInfo = ({ user }) => (

ワイ「↑こう書いてあるから」
ワイ「このUserInfoコンポーネントは」
ワイ「親コンポーネントからuserっていうpropsを受け取ってる...」
ワイ「っちゅうことが分かるわ」

ワイ「他にも...」

/components/UserInfo.jsx
<h2>ユーザ情報</h2>
<p>アカウント名:{user.account}</p>
<p>名前:{user.name}</p>

ワイ「↑こんなコードがあるから」
ワイ「親から受け取ったuserっていうpropsは、accountnameというプロパティを持ったオブジェクトである...」
ワイ「ってこともわかるで」

ワイ「あとは...」

<h2>記事一覧</h2>
<ol>
  {
    user.articles.map(article => (
      <li>
        <a href={article.url}>{article.title}</a>
      </li>
    ))
  }
</ol>

ワイ「↑こんな部分があるな...」
ワイ「せやから、userarticlesっちゅうプロパティも持っとんな」
ワイ「mapメソッドを使っているということは、そのuser.articlesは配列やということが分かるで」
ワイ「ほんで、その配列の中身はオブジェクトやな」
ワイ「urltitleというプロパティを持ったオブジェクトっちゅうことや」

ハスケル子「はい、そんな感じですよね」
ハスケル子「まとめると、どんな風に使うべきコンポーネントだと言えますか?」

ワイ「ええ...?」
ワイ「まあ、まとめるならば」

const user = {
  account: 'Yametaro',
  name: 'やめ太郎',
  articles: [
    {
      title: "記事名1",
      url: "/article1.html"
    },
    {
      title: "記事名2",
      url: "/article2.html"
    }
  ]
};

ワイ「propsとして↑こんなオブジェクトを渡して使うべきやな」

ハスケル子「そうですね」

TypeScriptの場合はどうか

ハスケル子「次に、TypeScriptを使った場合のコードを見てみます」

/components/UserInfo.tsx
type Props = {
  user: User
}

type User = {
  account: string,
  name: string,
  articles: Article[]
}

type Article = {
  title: string,
  url: string
}

const UserInfo: React.FC<Props> = ({ user }) => (
  <div>
    <section>
      <h2>ユーザ情報</h2>
      <p>アカウント名:{user.account}</p>
      <p>名前:{user.name}</p>
    </section>
    <section>
      <h2>記事一覧</h2>
      <ol>
        {
          user.articles.map(article => (
            <li>
              <a href={article.url}>{article.title}</a>
            </li>
          ))
        }
      </ol>
    </section>
  </div>
)

ハスケル子「↑こんな感じです」

ワイ「ほら、やっぱ型が書いてあるぶん長いやん...」

ハスケル子「そりゃあコード量は少し増えますけど」
ハスケル子「可読性の面でメリットもありますよ」
ハスケル子「例えば...」

/components/UserInfo.tsx
type Props = {
  user: User
}

ハスケル子「↑ここを見ると」
ハスケル子「このコンポーネントはuserというpropsを受け取る」
ハスケル子「そしてそのuserUser型の値である」
ハスケル子「...ということが分かります」

ワイ「なるほどな、受け取るprops一覧がここで分かるわけやな」
ワイ「今回の例ではuser一個だけってことか」
ワイ「でも、なんやのUser型って」
ワイ「number型とかstring型なら知っとるけど」

ハスケル子「それは↓この部分に書いてあります」

/components/UserInfo.tsx
type User = {
  account: string,
  name: string,
  articles: Article[]
}

ハスケル子「User型の値は、accountという文字列と」
ハスケル子「nameという文字列...」
ハスケル子「そしてarticlesという配列を持っているよ、ってことがわかります」

ワイ「ほうほう、つまり...」
ワイ「User型っていうのはハスケル子ちゃんの自作の型ってこと?」

ハスケル子「そうです」
ハスケル子「自分で新しい型を定義して名前を付けることができるんです」

ワイ「ほー、なんか...」
ワイ「Userってのはこんなやつですよ、みたいな」
ワイ「世界観的なものが分かりやすいな」

ハスケル子「そうですね」

ワイ「ほんで、Userが持ってるaccountnamestring型なのは分かったけど」
ワイ「Article[]ってのはどういう型なん?」

ハスケル子「Article[]っていうのは」
ハスケル子「Article型の値が入ってる配列だよ、って意味です」

ワイ「Article型ってのは、また自作の型やな?」

ハスケル子「はい」

/components/UserInfo.tsx
type Article = {
  title: string,
  url: string
}

ハスケル子「↑この部分でArticle型を定義しています」

ワイ「なるほど」
ワイ「Article型の値は、titleurlというプロパティを持ってますよ...」
ワイ「ほんで、titleurlも文字列ですよ...」
ワイ「っちゅうことやな」

ハスケル子「そうです」

型が書いてあると、何が嬉しいか

ハスケル子「TypeScriptなしの場合は」
ハスケル子「コンポーネントの中身を全部読んで...」

「なるほど、このコンポーネントにはuserというオブジェクトを渡して使うんだな」
「そのuserは、accountnamearticlesというプロパティを持ってるんだな」
articlesmapメソッドを持っているようだから、配列だな・・・!」

ハスケル子「...っていうことがようやく分かったじゃないですか」

ワイ「せやな」

ハスケル子「でもTypeScriptありの場合だと」

/components/UserInfo.tsx
type Props = {
  user: User
}

type User = {
  account: string,
  name: string,
  articles: Article[]
}

type Article = {
  title: string,
  url: string
}

ハスケル子「↑この型定義の部分を見ただけで...」

const user = {
  account: 'Yametaro',
  name: 'やめ太郎',
  articles: [
    {
      title: "記事名1",
      url: "/article1.html"
    },
    {
      title: "記事名2",
      url: "/article2.html"
    }
  ]
};

ハスケル子「↑こんなpropsを受け取るコンポーネントだな!」
ハスケル子「ってことが分かるじゃないですか」
ハスケル子「コンポーネントの実装部分を読まなくても!」

ワイ「おお...」
ワイ「やっぱリーダブルやなぁ...」

社長「(何がやっぱやねん...)」

ワイ「仕事やと、一つの案件のコーディングを複数人で担当することも多いから」
ワイ「人の作ったコンポーネントを使う機会とかも多いしな」
ワイ「せやから、読み取りやすいのはええよなぁ」

ワイ「あと思ったんやけど」
ワイ「型ってなんか、コメントみたいな効果もあるんやね」
ワイ「このプロパティには文字列を入れてくださいな、みたいな」
ワイ「コード内に仕様書が書いてある感じがええな」

ハスケル子「そうですよね」
ハスケル子「しかも、ちゃんと守らなきゃ前に進めない...」
ハスケル子「強制力を持ったコメントですよね」
ハスケル子「型定義の通りにpropsを渡さないとエラーが出て、コンパイルが通らない...」
ハスケル子「だから、マウスでぽちぽちページを見て回る前にミスに気付けるんです」

ワイ「ああ、普通はコードを実行してみるまで」
ワイ「エラーが出るかどうか分からへんもんなぁ...」
ワイ「それはええかも...」

他にも嬉しいことが

ハスケル子「そういえば、やめ太郎さんって」
ハスケル子「nameっていう単語をよくnamaeってスペルミスするじゃないですか」

ワイ「まぁ、比較的するな」

ハスケル子「そういうタイポ対策にもTypeScriptが役立ったりしますよ」
ハスケル子「例えば...」

/components/UserInfo.tsx
<p>名前:{user.namae}</p>

ハスケル子「↑こんな風にタイポしちゃったとしたら...」

Property 'namae' does not exist on type 'User'. Did you mean 'name'?
(プロパティ「namae」はタイプ「User」に存在しません。 「name」という意味ですか?

ハスケル子「なんて教えてくれるんです」

ワイ「おお、まじか...」
ワイ「まさにワイのための機能やん...」

ハスケル子「そもそもタイポしなくなるようなメリットもありますよ」

スクリーンショット 2020-01-12 22.14.27.png

ハスケル子「↑こんな風に、user.まで打つと入力候補が出てくるので」
ハスケル子「その中から選択すればいいんです」

ワイ「おお...」
ワイ「もう、使わん理由ないやん...」

ワイ「ハスケル子先生...!!」
ワイ「TypeScriptがしたいです......」

社長「ほな、決まりやな!」

そんなこんなで終業時刻

ワイ「ハスケル子ちゃん、今日も色々教えてくれてありがとうやで...」

ハスケル子「教えるの楽しいから全然いいですよ」

ワイ「ハスケル子ちゃんは凄いなぁ...まだ中学生なのに色々知ってて...」
ワイ「チート過ぎるハスケル子ちゃんを見てると、自分が情けなくなってくるわ...」
ワイ「負け過ぎてるんやもん...」

ハスケル子「私もよく思いますよ」
ハスケル子「私だけ周りの子たちより出遅れてて不安になったり、嫉妬したりします」
ハスケル子「でも結局」
ハスケル子「今の自分から生きることしかできないんです」
ハスケル子「誰かに負けてても、誰かより劣っていても」
ハスケル子「自分なりの精一杯をやればいいんです」

ワイ「ハスケル子ちゃん...!」

ハスケル子「...もしくは、嫌なら死ぬとか...」

ワイ「いや死を提案すな!

〜おしまい〜

続編もよろしくやで

4歳娘「パパ、実行時エラーの出ないフロントエンド言語ってなーんだ?」

さらに続編

テストしやすい「純粋」な関数とは?

おまけ:簡単にReact + TypeScriptを始めるには

React + TypeScriptを触ってみたい人は、

npx create-next-app --example with-typescript my-app-name
cd my-app-name
npm run dev

↑この3つのコマンドを打ってから
http://localhost:3000/にアクセスするだけやで!
(もちろんNode.jsは先にインストールしといてや!)

696
377
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
696
377

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?