背景と実施したこと
next.jsのwebアプリケーションをjavascriptからtypescriptに変換しました。
typescriptをインストールして、あとは各変数のtypeを用意してあげれば問題ないだろう、というノリだったのですが
細か~い点で注意すべきところがったので備忘として残します。
使用している技術のver情報は以下の通りです。
- next: 12.0.7
- react: 17.0.2
- TailwindCSS: 3.01
- typescript: 4.5.4
目次
- 概要
- Typescriptのインストールとセットアップ
- ①Typescriptの導入とconfigファイルの作成
- ②型定義のインストール
- ③「Cannot use JSX unless the '--jsx' flag is provided」の対処
- ④拡張子がjsのファイルをtsxに変更する
- 各変数に型を追加する
- _app.tsxの例
- _document.tsxの例
- propsの受け渡し部分の例
- map関数の例
- (補足)interfaceとtypeどちらがいいのか問題
- まとめ
概要
- typescriptの導入はtypescriptそのものインストールと型定義のインストールの2つを行う必要がある。
- app.tsx,_document.tsxは決まりきった書き方があるのでそれを覚えてしまおう。
- propsではどんな変数が渡ってくるかをinterface(またはtype)で定義しよう。
Typescriptのインストールとセットアップ
導入の手順は以下の通り。
###①Typescriptの導入とconfigファイルの作成
npm install --save-dev typescript
これでtscコマンドを使えるようになる。
npx tsc --init
tsconfigが作成される。
No inputs were found in config fileがエラーとしてtsconfigに出るが「tsファイルがないよ!」といっているだけ。
これから作るので無視してOK
②型定義のインストール
node標準のモジュール、react標準のモジュール、もし他に使っているモジュールがあればそれらの型定義をインストールする。
import文の部分で型定義がないよのエラーでていればこれで解消できる
npm install --save-dev @types/node
npm install --save-dev @types/react
③「Cannot use JSX unless the '--jsx' flag is provided」の対処
JSXでコードを書いている箇所に「Cannot use JSX unless the '--jsx' flag is provided」のエラーが出ている場合は
tsconfig.jsonのcompileoptionを「preserve」から「react」に変更して、再度IDE(VSCodeなど)を再起動しよう。
※自分の場合どこかのタイミングでcompileoptionがpreserveに戻ったのでconfigを読み込めていなかっただけなのかもしれない。
そうなるとconfigを作ったあとにIDEを再起動するだけでもよいかも
④拡張子がjsのファイルをtsxに変更する
jsファイルをjsxに変更する。(正確にはJSXが混ざる可能性のあるファイルをjsxに変えて、JSXで書くことはないとわかっているものの拡張子はtsでよい)
拡張子を変えることで変数を宣言している部分ではエラーが出ると思うので、各変数について型を宣言することでエラーを解消していく。
各変数に型を追加する
大前提として、すべての変数について型を追加する必要はない。型推論によって暗黙的に定義されている場所は何もしなくてもよい(エディター上でエラーになっている箇所だけ対応すればOK)
いくつか例を示す。
_app.tsxの例
import { AppProps } from 'next/app'; //追加部分
function MyApp({ Component, pageProps }:AppProps) { //:AppPropsを追加
return <Component {...pageProps} />;
}
export default MyApp;
デフォルトで作成されているファイルで使われている変数の型はどう定義するか迷うかもしれないが
next/appからimportしたAppPropsを型として使う。
_document.tsxの例
import Document, { Html, Head, Main, NextScript,DocumentContext } from 'next/document'; //DocumentContextを追加
class MyDocument extends Document {
static async getInitialProps(ctx:DocumentContext) { //:DocumentContextを追加
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
//以下省略
ctxはDocumentContextをimportして型として使う。
このあたりは決まりきった形として覚えてしまってもいいかもしれない。
propsの受け渡し部分の例
propsでコンポーネント間の引数を渡すときは以下の様なイメージになる。
(interface を使ってPropsとして渡ってくる変数の型を決めて、想定外の値が渡されることを防ぐ)
interface Props {
list: { name: string; link: string; }[]
}
export default function Header(props:Props) {
const [openMenu, setOpenMenu] = useState(false);
const data = props.list;
const menuFunction = () => {
setOpenMenu(!openMenu);
};
map関数の例
map関数を使う場面では以下の様なイメージになる。
(indexは配列の要素の数を返すのでnumber、valueは繰り返す対象のobjctになるのでこれもinterfaceを作る)
interface menuList {
name: string; link: string;
}
//途中省略
{data.map((value:menuList, index:number) => (
<li key={index} className='p-2 border-b-2'>
<a href={value.link} onClick={menuFunction}>
{value.name}
</a>
</li>
))}
(補足)interfaceとtypeどちらがいいのか問題
ちなみに上記のinterfaceの部分は
type menuList = {
name: string; link: string;
}
という形でも書ける。
※typeとinterfaceでは細かい違いがあり、どちらを使うべきかは非常に難しい問題となっているがここでは本論とずれるので割愛する。
■参考
TypeScriptのInterfaceとTypeの比較
interfaceとtypeの違い、そして何を使うべきかについて
まとめ
typescriptの導入の手順から、型の定義まで少し細かい点を記載してみました。
あらためて見ると注意すべき点は
- typescriptの導入はtypescriptそのものインストールと型定義のインストールの2つを行う必要がある。
- app.tsx,_document.tsxは決まりきった書き方がある。
- propsではどんな変数が渡ってくるかをinterface(またはtype)で定義する。
の3つかと思います。
また変数の型を参照するためにはtypeofの関数も用意されているので、「あれ?この変数はどんな型なんだろう」と思ったときはtypeofで確認してみるといいでしょう。