TypeScript入門


こんにちは。


みなさん。TypeScript使っていますか?
私は最近ぼちぼち使い始めました。でもまだまだ使いこなせていないなあ〜という感じです。

ただ、使いこなせていない自分でも、もうTypeScriptのない開発には戻れないなあという印象です。
初心者だからこそ、TypeScriptによる開発はメリットが多いです。
コンパイルエラーを教えてくれるので、エラーがでた箇所が特定しやすいなど、初心者だからこそ享受できるメリットも多いはずです。

しかし、なんかTypeScriptというと「型」を覚えたり、「型パズル」なんて言葉があったり、、、とっつきにくい印象がありますよね。
でも、安心してください!
React + TypeScriptで開発を行えば、まずはゆる〜く簡単にTypeScriptを導入することができます!
今回はmicroCMSで作成したAPIに簡単に型をつけるだけ!の簡単〜なTypeScriptをやっていきましょう!

題材として、ありがちなブログサービスを作成していこうと思います。

前提知識



microCMSのアカウントを持っている。APIの作成方法を知っていることを前提にしています。
知らない方は以下の記事をご覧ください。
note りゅーそうが作成したJamstack tutorial
上記の記事内に引用されている記事もとてもためになるので、読んでおくと理解が深まると思います。

また、当記事ではTypeScriptの入門と書いてありますが、文法・型の詳細な説明はしません。あくまで、TypeScriptを使って見ることを目的としています。TypeScriptの型は以下の記事がとても参考になると思います。
TypeScript 型入門

Next.jsv9~で解説を行います。microCMS + Next.jsの開発はかみむらさん(@kamimura_th)の記事が参考になるので読んでみてください。
Next.js + microCMS + NetlifyでJAMstackな世界に入門する

環境構築



まずはmicroCMSでAPIを作成します。作成方法は上記の記事を参考に。
今回は以下のようなAPIスキーマを作成してみました。

コンテンツ
ブログ

コンテンツのAPIスキーマ


※contentの種類は正しくはテキストエリアでした(すいません)。


タグ
タグのAPIスキーマ


今回はこのAPIを前提に話を進めます。
では、フロント側を準備していきましょう。
プロダクトの雛形を作っていきます。

npm init -y



そして、Next.jsに必要なパッケージをインストールしていきます。

npm install next react react-dom



TypeScriptの開発に必要なパッケージのインストール

npm install --save @types/react @types/react-dom @types/node typescript



TypeScriptの設定



TypeScriptのコンパイル設定を行うtsconfig.jsonファイルを作成します。
ブラウザではTypeScriptのままでは、コードを読み込むことができません。ブラウザが読み込めるようにJavaScriptに変換して上げる必要があります。
しかし、心配することはありません。Next.jsはTypeScriptのコンパイルを行う機能が備わっているので、tsconfig.jsonを作成することで簡単にコンパイルの設定を行うことができます。

mkdir tsconfig.json


また、

tsc --init




を入力することで、デフォルトの設定込みのファイルを作成することもできます。

Next.jsの公式のexampleを参考に以下のようなファイルを作成しました。
Github zeit/next.js/example

tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "lib": ["dom", "ES2017"],
    "jsx": "preserve",
    "allowJs": true,
    "noEmit": true,
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true
},
    "exclude": ["node_modules"],
    "include": ["src/**/*.ts", "src/**/*.tsx"]
}



設定について詳しくは、公式をご覧ください。
TypeScript Docs
ポイントはstrict:trueの設定を行なっていることです。この設定により、「any」型を厳密にチェックすることができ型を意識した開発を行うことができます。

また、package.jsonのscriptに以下の設定を行います。

"scripts": {
  "dev": "next",
  "build": "next build",
  "export": "yarn run build && next export",
  "start": "next start",
  "type-check": "tsc --watch"
},


基本的にはyarn dev コマンドを実行し、ブラウザで実行状況をみながら開発を行うと良いでしょう。
実際のアプリケーション開発では、webpackの設定を行なったりする必要があるので導入のハードルは高いですが、Next.jsのSSG開発においては以上で終わりです。このあたりもTypeScriptの導入に適していると思います。



APIデータに型定義を行う




これで、環境は整ったので進めていきましょう。
実際にmicroCMSで作成したAPIに型の情報を与えていきます。
microCMSのAPIリファレンスのページでAPIを試しに叩いてみましょう。(実際にコンテンツをいくつか作成しておいてください。)
こんな感じのレスポンスがあれば成功です。
APIリファレンス < APIを試して見る からcurlコマンドが叩けます。

{
  "contents": [
    {
      "id": "ryusou-blog-start",
      "createdAt": "2020-02-15T06:26:10.221Z",
      "updatedAt": "2020-02-20T14:13:48.580Z",
      "title": "りゅーそうブログを支える技術",
      "tags": [
        {
          "id": "qrpYw36Vr",
          "createdAt": "2020-02-12T12:25:02.648Z",
          "updatedAt": "2020-02-12T12:25:02.648Z",
          "name": "ブログ開発"
        }
  ],
  "day": "2020-02-15T07:00:00.242Z",
  "image": {
    "url": "https://images.microcm....png"
  },
  "content": "<p>こんにちは。..."
},
{
  "id": "ryusou-past-posts",
  "createdAt": "2020-02-12T13:43:14.085Z",
  "updatedAt": "2020-02-20T14:14:13.281Z",
  "title": "りゅーそうの過去の投稿まとめ",
  "tags": [
    {
      "id": "HFQzOpOdm",
      "createdAt": "2020-02-12T12:24:13.559Z",
      "updatedAt": "2020-02-12T12:24:13.559Z",
      "name": "コラム"
    }
  ],
  "day": "2020-02-13T12:00:00.000Z",
  "image": {
    "url": "https://images.microcms-assets.io....png"
  },
  "content": "<p>りゅーそうの過去のコンテンツの紹介です。....."
},
{
  "id": "ryusou-tech-blog-greed",
  "createdAt": "2020-02-12T13:11:26.392Z",
  "updatedAt": "2020-02-20T14:17:18.422Z",
  "title": "ごあいさつ(当ブログについて)",
  "tags": [
    {
      "id": "HFQzOpOdm",
      "createdAt": "2020-02-12T12:24:13.559Z",
      "updatedAt": "2020-02-12T12:24:13.559Z",
      "name": "コラム"
    }
  ],
  "day": "2020-02-13T12:00:00.000Z",
  "image": {
    "url": "https://images.microcms-assets.i.......png"
  },
  "content": "<p><br><br>こんにちは。......."
],
  "totalCount": 3,
  "offset": 0,
  "limit": 10
}



これをみながら欲しい型の情報を与えるだけです。
Next.jsはsrcディレクトリがサポートされているので、作成しましょう。

mkdir src


type定義のファイルを作成します。

[src/types/index.ts]
export type Post = {
  id: string;
  title: string;
  tags: [
    {
      id: string;
      name: string;
    },
  ];
  day?: Date;
  image: {
    url: string;
  };
  content: string;
};



先ほどのAPIをみながら、型の情報を与えていくだけです。簡単ですね。(ほとんどstring型...)
dayの横にある?はこの値はなくても大丈夫だよ〜。という意味です(Date型は出力する際にライブラリなどを使って変換して上げる必要があるので、今回はこのような設定にしてあります。)。

これを参照しながら、開発を進めていきます。



Next.jsのルーティング




Next.jsはpagesディレクトリがそのままルーティングされる仕様になっています。とても便利ですね。例えば、local環境で開発していた場合、src/pages/index.tsxとすると、localhost8000:/ となります。src/pages/aboutとすると、localhost8000:/aboutのようになります。これを利用して、コンテンツを取得するページを作成しましょう。

  • src/pages/posts/index.tsx ・・・記事一覧ページ
  • src/pages/posts/[id].tsx ・・・個別記事ページ





axiosでデータを取得





では、作成したフォルダ内でデータの取得を行なっていきましょう。
APIを叩くためにaxiosをインストールします。axiosはパッケージ内部で型定義がされているので、@typesのインストールは必要ありません。

npm install axios


microCMSのAPIを叩くために、headerにmicroCMSのX-API-KEYを入れます。フォルダを作成して、以下の設定を行います。環境変数はdotenvなどを使用して.envファイルに入力しましょう。
dotenv

[src/lib/api.ts]
import axios from 'axios';

export const axiosInstance = axios.create({
  method: 'get',
  headers: {
    'Content-Type': 'application/json',
    'X-API-KEY': process.env.api_key,
  },
});


これを利用して、APIを取得します。



記事一覧ページの作成




[src/pages/index.tsx]
import * as React from 'react';
import { NextPage } from 'next';
import Link from 'next/link';

import { axiosInstance } from '../../lib/api';
import { Post } from '../../types';


type Props = {
  posts: Post[];
};

const BlogsPage: NextPage<Props> = ({ posts }) => {
  return (
    <>
      <h2>BLOG 一覧</h2>
      <div>
        {posts.map(post => (
          <React.Fragment key={post.id}>
            <Link href={`posts/${post.id}`}>
              <a>
                <h2>{post.title}</h2>
              </a>
            </Link>
            {post.tags.map(tag => (
              <React.Fragment key={tag.id}>
                <span>{tag.name}</span>
              </React.Fragment>
          ))}
          </React.Fragment>
        ))}
      </div>
    </>
  );
};


PostsPage.getInitialProps = async () => {
  const res = await axiosInstance.get(
    `https://ryusou-mtkh.microcms.io/api/v1/posts/`,
  );
  const data: Post[] = await res.data.contents;
  return { posts: data };
};

export default PostPage;



ポイント

  • 先ほどのpagesのルーティングを利用してリンクを作成するためには、next/linkを利用します。
  • Propsの型に先ほど作成したPostの型情報を与えます。この型情報を型に与えて上げることによって、中で取得したデータに型を与えることができます(なぜ、Post[] ←配列なのか考えてみてください。microCMSで作成したデータは配列になっています。)。
  • また、APIデータを取得する際にはgetInitialPropsを利用するのが便利です。このようにすることによって、処理をみやすく記述することができます。getInitialPropsを利用する際にはNextPageを関数の型に定義しましょう.


こうすることで、gitInitialPropsにまで、型の情報を与えることができます。getinitialPropsにカーソルを当てると、contextにNextPageContentを与えられているのがわかります(※エディタがVSCodeの場合)し、getInitialPropsの中にPromiseの型を与えることができているので、どのような処理をしたら良いのかというのが型の情報から得ることができます。
TypeScriptの型の情報はこのようにドキュメントのように利用することができます。
(getInitialPropsの中ではPromiseの処理を行うんだな〜という感じです。)
この型情報は個別記事取得の際にはさらに役立ちます。



個別記事ページ作成





先ほどのコードの中で、

<Link href={`posts/${post.id}`}>



というコードがありましたね。このように[id]の情報をそのままルーティングに当てることができます。Next.js便利ですね。

//[src/pages/[id].tsx] ← このように記述することで、与えられたコンテンツidにページ遷移することができる!

import * as React from 'react';
import { NextPage } from 'next';

import { axiosInstance } from '../../lib/api';
import { IPost } from '../../interfaces';


type Props = {
  post: IPost;
};

const PostContent: NextPage<Props> = ({ post }) => {
  return (
    <>
      <h1>{post.title}</h1>
      <div>
        {post.tags.map(tag => (
          <React.Fragment key={tag.id}>
            <span>{tag.name}</span>
          </React.Fragment>
        ))}
      </div>
      <img src={post.image.url} />
      <div dangerouslySetInnerHTML={{ __html: `${post.content}` }}></div>
    </>
  );
};


PostContent.getInitialProps = async context => {
  const { id } = context.query;
  const res = await axiosInstance.get(
    `https://ryusou-mtkh.microcms.io/api/v1/posts/${id}`,
  );
  const post: Post = await res.data;
  return { post };
};

export default PostContent;




基本的には先ほどのコードと一緒ですが、

  • 個別のデータが入ってきているので、Post型を関数に与えます(Post[]ではないです)。
  • 先ほどみたように、Next.jsのgetInitialPropsはcontextを受け取ります。これにも型の情報が与えられてます。これをURLにidとして与えて上げることによって、ルーティングに一致したデータを与えることができます。


これで最低限のブログサービスを配信することができました!




まとめ



このようにWEBサイト制作においても気軽にTypeScriptを導入することができるのが、わかるかと思います。React/Next.jsの学習コストはもちろんありますが、取得できればこのように簡単にサイト作成を行うことができます。
ぜひ、React/Next.jsのシステムを活用して、TypeScriptを気軽にサイト制作にも導入してみてはいかがでしょうか?

また、microCMSはUIもみやすく、取得できるAPIの情報がとてもわかりやすいので、今回のように新しい技術を学ぶ際にやりやすいツールだと思います。
この記事をきっかけにTypeScriptに挑戦してみようかなという方がいらっしゃると嬉しいです!

今回作成したサイトのコードはこちらになります!合わせて参照してください!
GitHub

最後まで読んでくださり、ありがとうございました!