はじめに
前回の記事でlaravelにVite経由で導入したReactにTypeScriptを移行したので、続きで既存コードを書き換えていこうと思います。
TypeScriptの移行の記事↓
https://qiita.com/kuro_maru/items/fcfba1ebe46b4b6f8cfa
そもそも、なぜTypeScriptを使用するのか?
参考サイト⇨https://www.youtube.com/watch?v=X6haQpKrooM
4:00くらいからの解説がなぜ必要なのかの説明がありわかりやすかったです。
TypeScriptの基本の型
interfaceとtypeの使い所
interface ⇨ オブジェクトを定義する際に使用する、配列要素に使用する際は、sampleInterFace[]のように記述する。
type ⇨ 既存の型を基に、新しい型を作成するために使用する。
(Union型や複雑な型を定義するのに適している)
書き換え作業
eventの型をどうするか
例えば以下のようなコードがあるとして、
// 入力内容の反映
const handleChange = (event) => {
console.log(event);
const { name, value } = event.target;
setScheduleFormData({ ...scheduleFormData, [name]: value});
};
eventに何も設定しなかったら自動でany型になるが、それもどうかと思ったので調べてみました。
以下の記事がとてもわかりやすかったです⇩
https://qiita.com/y-suzu/items/9e9a243aaded5952834a
console.log()で実際に調べてみました。
// 入力内容の反映
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
console.log(event);
const { name, value } = event.target;
setScheduleFormData({ ...scheduleFormData, [name]: value});
};
同フォーム内で入力が分かれている場合
<label className={ Schedule.schedule__label }>
要件:
<input type="text"
name="requirement"
placeholder="内容を入力してください"
className={ Schedule.schedule__content }
onChange={ handleChange }
/>
</label>
<label className={ Schedule.schedule__label }>
メモ:
<textarea className={ Schedule.schedule__content }
name="memo"
onChange={ handleChange }
>
</textarea>
</label>
このようにinput/textareaで入力が分かれている場合は、両方とも検知対象にするため、以下のようにChangeEventHandlerに渡します。
HTMLInputElement | HTMLTextAreaElement
// 入力内容の反映
const handleChange: ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement> = (event) => {
const { name, value } = event.target;
setScheduleFormData(prevData => ({
...prevData, [name]: value
}));
};
ChangeHandlerはreactのonChange()が持つイベントオブジェクトです。
公式GitHub⇨https://github.com/search?q=repo%3Afacebook%2Freact%20ChangeEventHandler&type=code
上記を記述しましたが今回は、送信対象のフォームデータに型を付けたのでイベントに型は付けていません。
レスポンスデータの型
今回、レスポンスデータ全体と中身に型を付けたいことがあり調べるとこのような書き方がありました。
以下だと、extendedResponseDataはinitialResponseFormの拡張版となっています。
// レスポンスデータ内のformFilterDataの型
export interface initialResponseForm {
id: number;
requirement: string;
memo: string;
cleated_at: string;
};
// レスポンスデータの型
export interface extendedResponseData extends initialResponseForm {
formFilterData: [];
message: string;
selectedDate: string;
};
今回よく出てきたuseState()
useStateは、値の初期値を設定する際に、型推論をしてくれるが何のデータが入ってくるか分からない場合は明示的に型を指定するほうがいいのかなと色々調べていて感じました。
// 指定している型は、上記の型です
const [responseViewData, setResponseViewData] = useState<extendedResponseData>();
const [formToFilterData, setFormToFilterData] = useState<initialResponseForm[]>();
明らかにデータ型がわかるところには、型を明示的にしませんでした。
// 予定作成フォームの状態を取得
const [openForm, setOpenForm] = useState(false);
非同期処理の関数の型
// 非同期処理の宣言
type passToResponseDataFunc = (data: extendedResponseData) => Promise<void>;
export interface propsFunc {
passToResponseData: passToResponseDataFunc;
};
passToResponseDataFuncで、extendedResponseData型を持つdataを引数に受け取り、Promiseを返す = つまり、非同期処理の宣言を行い、propsFuncでpassToResponseDataに非同期処理の型を付けています。
戻り値の型にPromise voidを指定しているのは、
const passToResponseData: passToResponseDataFunc = async(data) => {
setResponseViewData(data);
setOpenForm(!openForm);
};
今回の場合だと、return文がないからです。
もし、戻り値がある場合はこのように設定できるそうです。
// T にnumberや対象のデータ型が入ります
Promise<T>
非同期処理の宣言については、以下の記事が参考になりました。
https://typescript-jp.gitbook.io/deep-dive/type-system/functions
propsの型
迷ったのが、React.FCの使用についてです。
const ScheduleForm: React.FC<propsFunc> = ({ passToResponseData }) => {
React.FCとは?
「そのコンポーネントが受け取るPropsの値の型を定義するために使用するもの」だそうで、コンポーネントが受け取るPropsの型や形式が明白になるそうです。
いいとこ尽くしなような気がしましたが、これから開発を続けていく上で、再利用する場面も出てくるかも知れないなと思い、React.FCを使用しなくても定義できるようなので今回は使用はしませんでした。
// 定義できました
const ScheduleForm = ({ passToResponseData }: propsFunc) => {
意見が参考になった記事⇩
https://qiita.com/islandryu/items/6477ff6a3a9d74a7cc8c
まとめ
今回、初めはjavascriptで記述したものをtypescriptで型指定するような方法をとりましたが、strictモードのおかげでしっかりエラーが出てくれました。
修正部分に疑問も圧倒的な知識不足を感じたので、引き続きtypescriptで開発を続けていこうと思います。