使用主言語をJavaScriptからTypeScriptへ転換しようと試みている、そのための大雑把な備忘録リスト。
(なおAngular version7, 8でTypeScriptを書いていたが、Angularはすべてクラス型コンポーネントだったしやはりReactとは違いが色々ありすぎでReact系をTypeScriptで…となると当時の知識は使えない)
準備編
TypeScriptインストールコマンド
npm install typescript @types/node @types/react @types/react-dom
react-router-domをインストールしていたらさらに
npm install @types/react-router-dom
tsconfig.json作成コマンド
npx tsc --init
スタンダードなtsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}
JavaScriptとの比較編
デフォルトのindex.jsファイルをTypeScriptにする(index.js → index.tsx)
- JavaScript
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App />
);
- TypeScript
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<App />
);
as HTMLElement
を追加
ステート変数宣言
- JavaScript
const [loading, setLoading] = useState(false);
const [city, setCity] = useState("");
- TypeScript
const [loading, setLoading] = useState<boolean>(false);
const [city, setCity] = useState<string>("");
ステート変数宣言(複数をまとめる形式)
- JavaScript
const [results, setResults] = useState({
country: "",
cityName: "",
temperature: 0,
conditionText: "",
})
- TypeScript
type ResultsStateType = {
country: string;
cityName: string;
temperature: number;
conditionText: string;
}
const [results, setResults] = useState<ResultsStateType>({
country: "",
cityName: "",
temperature: 0,
conditionText: "",
icon: "",
})
type
で型定義宣言。ResultsStateType
は任意の名前。ResultsStateType
でいくつかのステート変数をまとめて型定義している.
ステート変数宣言(配列形式)
- JavaScript
const [allCountriesData, setAllCountriesData] = useState([])
- TypeScript
type AllCountryDataType = {
Country: string,
NewConfirmed: number,
TotalConfirmed: number,
}
const [allCountriesData, setAllCountriesData] = useState<AllCountriesDataType>([{
Country: "",
NewConfirmed: 0,
TotalConfirmed: 0,
}])
JavaScriptならカラの配列である、というだけで何も文句は言われない。TypeScriptでは具体的にどんな要素データが配列に入るか、まずは明示する必要がある。
const [allCountriesData, setAllCountriesData] = useState([{
Country: "",
NewConfirmed: 0,
TotalConfirmed: 0,
}])
その上で、以下のようにtype
で型定義する。
type AllCountryDataType = {
Country: string,
NewConfirmed: number,
TotalConfirmed: number,
}
ステート変数宣言(配列形式)― type
の代わりにinterface
を使う場合
- TypeScript
interface SingleCountriesDataType {
Country: string,
NewConfirmed: number,
TotalConfirmed: number,
}
interface AllCountriesDataTypeArray extends Array<SingleCountriesDataType>{}
const [allCountriesData, setAllCountriesData] = useState<AllCountriesDataTypeArray>([{
Country: "",
NewConfirmed: 0,
TotalConfirmed: 0,
}])
ひとつ目のinterface
で定義しているのは一個の{}データなのでSingleCountriesDataType
という名前にしている(分かりやすくするためだけ)。別にAllCountriesDataType
でもCountriesDataType
でも良い。
そして
AllCountriesDataTypeArray extends Array<SingleCountriesDataType>{}
によって、AllCountriesDataTypeArray
がSingleCountriesDataType
を拡張した配列であることを明示している。
最後にくっついている{}
はおまじない的なオマケ。
関数に渡されるFormのイベントe
- JavaScript
const getInfo = (e) => {
e.preventDefault();
// ・・・省略・・・
}
- TypeScript
const getInfo = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// ・・・省略・・・
}
フォームイベントe
は、Reactが定義している型React.FormEvent<HTMLFormElement>
となる
propsに渡されたステート関数(ここではprops.setCity
)
- JavaScript
(propsを渡してる側のコンポーネント)
const [city, setCity] = useState("");
// ...
<Form setCity={setCity} getInfo={getInfo} />
(propsを渡されてる側のコンポーネント)
const Form = (props) => {
return (
<form>
<input type="text" name="city" onChange={e => props.setCity(e.target.value)} value={props.city} />
<button type="submit">Submit</button>
</form>
);
};
- TypeScript
(propsを渡してる側のコンポーネント)
const [city, setCity] = useState<string>("");
// ...
<Form setCity={setCity} getInfo={getInfo} />
(propsを渡されてる側のコンポーネント)
type FormPropsType = {
city: string;
setCity: React.Dispatch<React.SetStateAction<string>>;
}
const Form = (props: FormPropsType) => {
return (
<form>
<input type="text" name="city" onChange={e => props.setCity(e.target.value)} value={props.city} />
<button type="submit">Submit</button>
</form>
);
};
定義されたFormPropsType
をprops
に設定(props: FormPropsType
)。
ステート関数setCity
の型はReact.Dispatch<React.SetStateAction
というもので、useState
の内部で行われる操作系型らしい。。。
propsに渡された関数(ここではprops.getInfo
)
- JavaScript
(propsを渡してる側のコンポーネント)
const getWeather = (e) => {
// ...処理
}
// ...省略...
<Form setCity={setCity} getInfo={getInfo} />
(propsを渡されてる側のコンポーネント)
const Form = (props) => {
return (
<form onSubmit={props.getInfo}>
<input type="text" name="city" onChange={e => props.setCity(e.target.value)} value={props.city} />
<button type="submit">Get Info</button>
</form>
);
};
- TypeScript
(propsを渡してる側のコンポーネント)
const getWeather = (e: React.FormEvent<HTMLFormElement>) => {
// ...処理
}
// ...省略...
<Form setCity={setCity} getInfo={getInfo} />
type FormPropsType = {
city: string;
setCity: React.Dispatch<React.SetStateAction<string>>;
getInfo: (e: React.FormEvent<HTMLFormElement>) => void;
}
const Form = (props: FormPropsType) => {
return (
<form onSubmit={props.getInfo}>
<input type="text" name="city" onChange={e => props.setCity(e.target.value)} value={props.city} />
<button type="submit">Get Info</button>
</form>
);
};
e
が無ければgetInfo: => void;
で良い(void型)。しかしフォームイベントe
があるからgetInfo: (e: React.FormEvent<HTMLFormElement>) => void;
となる。
propsに渡されたjsonデータ(ここではprops.countriesJson
)
(propsを渡してる側のコンポーネント)
<TopPage countriesJson={countriesJson} setCountry={setCountry} />
- JavaScript
const TopPage = (props) => {
return (
<div>
<Selector countriesJson={props.countriesJson} setCountry={props.setCountry}
</div>
);
};
- TypeScript
type TopPageType = {
countriesJson:{
Country: string,
Slug: string,
}[],
setCountry: React.Dispatch<React.SetStateAction<string>>,
}
const TopPage = (props: TopPageType) => {
return (
<div>
<Selector countriesJson={props.countriesJson} setCountry={props.setCountry}
</div>
);
};
ここではJSONデータの要素(Country, Slug)が共にstring型。また、JSONでは複数の{}が[]に挟まれている形式であるがゆえに[],
を追加する必要がある。React.Dispatch<React.SetStateAction<string>>
は前述(propsに渡されたステート関数)
備考:型定義をひとつのファイルにまとめる
大規模開発ではひとつのファイルにまとめてしまう事があるらしい。
- 手順
1.場所はどこでも良いがtypes.tsファイル(名前は任意)を新規作成する。
2.各コンポーネントで定義した型定義コードをtypes.tsに移す(カット&ペースト)。
3.types.tsにペーストしてきた型定義コードは必ずexport
させる。
例:↓
export type CountryDataType = {
date: string,
newConfirmed: number,
totalConfirmed: number,
newRecovered: number,
totalRecovered: number,
}
4.カットされた各コンポーネントでは、使用していた型定義をtypes.tsからインポートする。
例:↓
import { CountryDataType, AllCountriesDataTypeArray } from "./type"
◆参考:[はじめてさわるReact & JavaScript with TypeScript Kindle版 三好アキ著]
本の宣伝
Gatsbyバージョン5>>>>改訂2版
前編の『Gatsby5前編ー最新Gatsbyでつくるコーポレートサイト』と後編の『Gatsby5後編ー最新GatsbyとmicroCMSでつくるコーポレートサイト《サイト内検索機能付き》』を合わせ、次のようなデモサイトを構築します。
→ https://yah-space.work
静的サイトジェネレーターGatsby最新バージョン5の基本とFile System Route APIを使用して動的にページを生成する方法を解説。またバージョン5の新機能《Slicy API》《Script API》《Head API》を紹介、実装方法も。《Gatsby Functions》での問い合わせフォーム実装やGatsby Cloudへのアップロード方法も!
Gatsby5前編ー最新Gatsbyでつくるコーポレートサイト ~基礎の基礎から応用、新機能の導入まで(書籍2,980円)
最新Gatsby5とmicroCMSを組み合わせてのコーポレートサイト作成手順を解説。《サイト内検索機能》をGatsbyバージョン4からの新機能《Gatsby Functions》と《microCMSのqパラメータ》で実装。また、SEOコンポーネントをカスタマイズしてmicroCMS APIをツイッターカードに表示させるOGPタグ実装方法も解説。
Gatsby5後編ー最新GatsbyとmicroCMSでつくるコーポレートサイト《サイト内検索機能付き》(書籍 2,790円)