はじめに

私はReact + TypeScriptを組み合わせた個人プロジェクトに取り組んでおり、type データ型についていくつかの疑問が生じました。それらはどのように機能し、いつ必要になるのでしょうか?いつ使用すべきで、いつ使用すべきでないのでしょうか?

このプロジェクトは、公開API PokeAPI を使用してポケモン図鑑を作成することです。これまでに以下のものがあります:

  • メイン検索バー -> ポケモンの名前または図鑑番号で検索できます。何も入力しない場合は、すべての可能なレコードの中からランダム検索を行います。
  • メインテーブル -> 最初に10匹のポケモンといくつかのシンプルな統計を表示します(後でさらに追加されます)
  • 詳細ページ -> 検索されたポケモンの画像、名前、基本統計を表示します。
  • モバイル専用のデザイン、後でタブレットとデスクトップ用の対応バージョンを追加します。Mobile-Firstデザインパターンに従うようにしています。

動作をよりよく理解するために、GIFを添付します:

Types

非常にシンプルです。それでは、私たちが興味を持っているTypesのトピックに入りましょう。TypeScriptで型を修正する簡単な解決策は「any」を使用することですが、それはアプリケーションで起こりうるエラーを遅らせるだけです。そのため、カスタム型の作成について調査を始めました。

この場合、最初に入力で目的のポケモンを検索し、URLを介して渡し、表示するコンポーネントで収集します:

interface SearchBarProps {
  searchTerm: string;
  onInputChange?: (event: React.ChangeEvent<HTMLInputElement>) => string | void;
}

export const SearchBar: React.FC<SearchBarProps> = ({
  searchTerm,
  onInputChange,
}) => {
return (
        <div className="SearchBar">
            <form>
                <span>Search for Pokemons</span>
                <input
                    type="search"
                    placeholder="Pikachu, Bulbasaur..."
                    value={searchTerm}
                    onChange={onInputChange}
                    required
                />
                {searchTerm !== '' ? (
                        <Link to={`/PokemonDetails/${searchTerm}`}>
                            <button>Search</button>
                        </Link>
                    ) :
                    <Link to={`/PokemonDetails/${randomNumber}`}>
                        <button>Search</button>
                    </Link>
                }
            </form>
        </div>
    );
}

ご覧のとおり、searchTermは2行目にあるインターフェースに従ってString型に対応しています。その文字列には検索するポケモンの名前が含まれており、ここまでは問題ありません。

その後、その文字列はReactのuseParams hookを使用してURL経由で別のコンポーネントに渡され、子コンポーネントでクエリを実行します:

interface PokeProps {
    searchTerm: string;
}

export const PokemonDetails: React.FC<PokeProps> = () => {
    const {PokemonURL: urlParam} = useParams();
    const [newPokemon, setNewPokemon] = useState<Pokemon>();

    useEffect(() => {
        setFlag(true);
        const client = new Client();
        const info = client.getInfo(`https://pokeapi.co/api/v2/pokemon/${urlParam.toLowerCase()}`);
        info.then((response) => {
            setNewPokemon((prevState) => ({
                ...prevState,
                name: response.data.name,
                sprites: response.data.sprites,
                stats: response.data.stats,
            }));
            setFlag(false);
        });
    }, [urlParam]);

    return (
        <div className="PokemonDetails">
            {flag ? (
                <div className="pokeball">
                    <div className="pokeball__button"/>
                </div>
            ) : newPokemon !== undefined ? (
                <ContentDetail pokemon={newPokemon}/>
            ) : (
                <div>me mori</div>
            )}
        </div>
    );
};

クエリが実行され、検索されたポケモンに関連するすべてのデータが返されます。ここで「問題」が発生します。最後にContentDetailというコンポーネントがあり、そこにポケモンを保存するフック => newPokemonを渡していることに注目してください。パラメータを渡すコンポーネントは、そのオブジェクトを受け取って型を設定する必要があります。そうしないと、よくあるエラー:type mismatchが発生します。

では、これをどう解決すればよいでしょうか? -> まず、Pokemonデータ型を作成する必要があります。私の場合は、Types.tsというファイルに抽出しました

export type Pokemon = {
  name: string;
  sprites: {
    front_default: string;
  };
  stats: {
    base_stat: number;
    stat: {
      name: string
    }
  }[];
};

データ型が確立されたら、API呼び出しからの応答を保存するstateに型を設定する必要があります

const [newPokemon, setNewPokemon] = useState<Pokemon>();

素晴らしい、片側の型付けができました。親では問題ありません。次に、子は受け取るオブジェクトがPokemon型であることを知る必要があります。インターフェースで簡単に示すことができます

interface ContentDetailProps {
    pokemon: Pokemon;
}

export const ContentDetail: React.FC<ContentDetailProps> = ({ pokemon }) => {
  return (
    <div className="ContentDetail">
      <p>Nombre: {pokemon.name}</p>
      <div>
        <img src={pokemon.sprites.front_default} alt="Hola" />
      </div>
      {
        <div className="PokemonInfo">
          {pokemon.stats.map((stat: any) => (
            <div className="PokemonStats">
              {console.log(pokemon)}
              <div>{stat.stat.name}</div>
              <div>{stat.base_stat}</div>
            </div>
          ))}
        </div>
      }
    </div>
  );
};

何を学んだか?

一般的に、インターフェースを使用して型を設定する方法と、型を別のファイルに抽出するという良い実践方法を学びました。将来、APIでその型にさらに多くのものが追加された場合、アプリケーションで単一のファイルで変更できるため、すべてを分離しておくことは非常に良いことです。