🌟

【学習メモ】TypeScriptを本気で理解しにかかる〜#2-2 実践編 React × TypeScript〜

に公開

概要

(#2-1)こちらの記事の続き(#2-2) です。
前回は TypeScript の基礎をざっと学んでいきましたが、今回は実践編として React プロジェクトに TypeScript を組み込む方法を見ていきたいと思います!

TypeScript 実践編

前提知識

前回 TypeScript の基本的な型は一通り見ていきました。
今回は前提知識として、Web アプリケーションを作成する上で必要となる型をまずは見ていきたいと思います。

Dom 操作と型

Web 開発をする際、JavaScript で Dom 操作をすることは頻繁にあると思います。
TypeScript は HTML コードの解析はできないため、Element を指定して操作する場合、その Element の種類を明示的に指定しないといけない場合があります。

Element の種類についてはこちらが大変参考になるかと思います。

これらの HTMLElement に対して TypeScript ではあらかじめ interface が用意されているので型指定できるというわけです!
https://microsoft.github.io/PowerBI-JavaScript/interfaces/node_modules_typedoc_node_modules_typescript_lib_lib_dom_d.htmlelement.html

例としては以下のように型アサーションしてやる必要があります!

const input = document.getElementById("username") as HTMLInputElement; //inputタグを指定
input.placeholder = "山田太郎";

or

const input = <HTMLInputElement>document.getElementById("username"); //inputタグを指定
input.placeholder = "山田太郎";

こちらは React では使えないので、非推奨

イベントの型

イベントにも型を指定する必要があります。

const target = document.getElementById("username");
target.addEventListener("input", (e) => {
  console.log(e);
  console.log(e.target.value);
});

上記のようにテキストボックスのイベントを拾って value を取得するというのはよくあると思いますが、TypeScript でこのまま記述するとエラーが発生します。

こちらのコンパイルエラーを回避する方法は以下の 2 とおりです。

①as でアサーションする

const target = document.getElementById("username") as HTMLInputElement;
target.addEventListener("input", (e) => {
  console.log(e);
  console.log((e.target as HTMLInputElement).value);
});

②instanceof で型チェックする

const target = document.getElementById("username") as HTMLInputElement;
target.addEventListener("input", (e) => {
  console.log(e);
  if (e.target instanceof HTMLInputElement) {
    console.log(e.target.value);
  } else {
    // 別途処理
  }
});

非同期処理・async 関数の戻り値(Promise)

async 関数の戻り値は Promise となりますが、こちらの戻り値にも型を定義してあげる必要があります。
こちらは定義しないとコンパイルエラーになるということはありませんが、定義しなかった場合、Promise で受け取るレスポンスの値が any になってしまうので、避けるべきだと思います。
また、戻り値を定義しておくと、その API を叩くとどんなデータが返ってくるのか明確になるので、開発の手助けになるかと思います!

type testJson = {
  username: string;
  age: number;
  hoge: boolean;
};

const test = async (): Promise<testJson> => {
  const res = await fetch("https://tes", { method: "get" })
    .then((response) => {
      if (response.ok) {
        return response.json();
      }
      throw new Error("error");
    })
    .catch((e) => console.log(e));
  return res;
};

上記の通り、Promise<Promiseで返すオブジェクトの型>といった形式になります。

React で TypeScript を使う

環境構築

npx create-react-app {プロジェクト名} --template typescript

上記のように--template typescriptを付加しておくことで typescript の環境を用意してくれます。
便利ですね!

また、React で TypeScript を扱うためにはファイル名をtsxとする必要があります。

関数コンポーネントの型

関数コンポーネントの型は React が用意してくれているReact.FCになります!

import React from "react";

export const Test: React.FC = () => {
  return <div>Test</div>;
};

ただし、型指定しなかった場合は、JSX.Elementが型になるようです。
どちらを使うべきかについてですが、こちらはネットで調べても色々論争があるようです。
(特に理由がなければJSX.Elementで良いかなと思いますが、まだまだここは勉強不足なので、使っていくうちに使い分けができるようにしたいなと思います!)

useState の型

useState の型を明示的に指定する場合は以下のようになります。

import React, { useState } from "react";

export const Test = () => {
  const [count, setCount] = useState<number>(0);
  return (
    <>
      <div>{count}</div>
      <button onClick={() => setCount((prev) => prev + 1)}>Click</button>
    </>
  );
};

useState<number>(0)

また、オブジェクトなどはあらかじめ型エイリアスで用意しておいた型を指定することもよくあるかと思います!

import React, { useState } from "react";

type Profile = {
  name: string;
  age: number;
};

export const Test = () => {
  const [mem, setMem] = useState<Profile>({
    name: "Bob",
    age: 20,
  });
  return (
    <>
      <div>{mem.name}</div>
      <div>{mem.age}</div>
    </>
  );
};

イベントの型

イベントの型は指定しないとコンパイルエラーになります。
まずは例として Input タグの change イベントを取得するコードを記載します。

import React, { useState } from "react";

type Profile = {
  name: string;
  age: number;
};

export const Test = () => {
  const handleInputName = (e: React.ChangeEvent<HTMLInputElement>) => {
    setMem((prev) => {
      return {
        ...prev,
        name: e.target.value,
      };
    });
  };

  const [mem, setMem] = useState<Profile>({
    name: "Bob",
    age: 20,
  });
  return (
    <>
      <input type="text" value={mem.name} onChange={handleInputName} />
      <div>{mem.name}</div>
      <div>{mem.age}</div>
    </>
  );
};

e:React.ChangeEvent<HTMLInputElement>
こちらが React でのテキストボックスにおける change イベントになります。
他にも
form タグの onSubmit イベント:React.FormEvent<HTMLFormElement>
button タグの押下イベント:React.MouseEvent<HTMLButtonElement>
などなどありますが、全部覚えられないですよね...

なので、こうゆうときは型推論させた結果を vscode 上で取得してコピペする技を使いましょう。
上記の例でいうと、onChange イベントに一度アロー関数で引数を取ります。
<input type="text" value={mem.name} onChange={(e) => handleInputName(e)}/>

この引数に対してマウスホバーすると、vscode が優しく教えてくれます!
vscodeイメージ

props に型をつける

親から子コンポーネントに props を渡す場面は多々あると思いますが、props の型定義は以下のようになります。

type Props = {
  message: string;
};

export const Test = (props: Props) => {
  return <div>{props.message}</div>;
};

もしくは分割代入の場合は以下

type Props = {
  message: string;
};

export const Test = ({ message }: Props) => {
  return <div>{message}</div>;
};

まとめ

これで React に TypeScript を組み込むための準備ができました。
フロントエンド開発で使用する分にはこれで問題なく開発ができるかなと思います。
一旦、私はフロントでしか JavaScript を使用する予定がないので、今月の TypeScript 学習もここまでにしようかと思います。
あとは、開発しながら慣れていくしかないかなと...!

GitHubで編集を提案

Discussion