この記事は、Wano Group Advent Calendar 2021 の記事です。
型は命、anyは敵?
「型は命、anyは敵」 とタイトルに書きましたが、anyが必要な場面ももちろんあると思います。避けるべきは不用意なanyによる型の握りつぶしです。
この前提をもとに可能な限り正しく型付けを行うことが、最大のコーディング規約です。
Reactファイルのテンプレート
半分結論に近いですが、先にReactのファイルのテンプレートを晒します。
何かしらのComponentを作成するとき、とりあえずこれを書いています。
import React from 'react';
interface Props {
className?: string;
}
const SampleComponent: React.VFC<Props> = ({ className }) => {
return (
<div className={className}>
SampleComponent
</div>
)
}
export default SampleComponent;
コーディング規約
原則としてComponentは1ファイル1つにし、ファイル名とComponent名を一致させる
コードの見通しを良くするためです。
(まれに例外として、小さなComponentをファイル内に定義することはあります。)
const SampleComponent: React.VFC<Props> = () => {} ;
Component の export には default export
を使い、 Named Exportしない
const SampleComponent: React.VFC<Props> = () => {} ;
export default SampleComponent;
tsxファイルでは、import { useEffect }
のような Named import はしない
import React するので、基本的に個別でのNamed Importはしないように揃えています。
// Good
import React from "react";
React.useEffect(()=>{ /* ... */ },[]);
// Bad
import React, { useEffect } from "react";
useEffect(()=>{ /* ... */ },[]);
styled-componentsは使わない
material-ui を採用しており、基本的に makeStyles を使っています。
頻度は低いですが、共通のClassを使いたい場合に共通のhookとして複数Componentから使い回すことも可能なのが利点です。
const useStyles = makeStyles(theme => {
root: {
marginTop: theme.spacing(2),
},
});
Component の props の型は明示的に宣言する
可読性向上が目的です。
// Good
interface Props {
className?: string;
};
const Component: React.VFC<Props> = (props) => {};
// Bad
const Component = (props: { className?: string }) => {};
型の定義(特にProps)には type
ではなく interface
を使う
多くの場合、Propsはシンプルな型なので interfaceで定義可能だと思っています。
ジェネリクスや型推論が必要であったり、よほど複雑な型の場合はtypeを使うことは許容していますが、よりシンプルな型にする意識を保つことで可読性も保ちやすいと考えています。
// Good
interface Props {
title: string,
className?: string,
};
// Bad
type Props = {
title: string
className?: string,
};
// OK - ただし、他のComponentに依存してよいかなどはレビューで議論する
type Props = {
title: string,
className?: string,
} & Partial<React.ComponentProps<OtherComponent>>;
Component の型定義には FC
ではなく VFC
を使う
FC
を使うと、 children
が optional として props に入ってしまいます。
VFC
を使い、明示的に children
の有無を指定することで、コーディングミスを減らすことが目的です。
同様の理由から、PropsWithChildren
を使うことも避けています。
// Good
interface Props {
className?: string;
children: React.ReactNode;
};
const Component: React.VFC<Props> = ({ children, className }) => {};
// Bad
interface Props {
className?: string;
};
const Component: React.FC<Props> = ({ children, className }) => {};
props は destruct で受ける
useEffect
などのhookの第二引数deps
にpropsそのものを指定しないように。
deps
で毎回 props.
とアクセスしないで良いように。
// Good
const Component: React.VFC<Props> = ({ className }) => {};
// Bad
const Component: React.VFC<Props> = (props) => {};
あとがき
ここに書いたものはあくまでも個人的なコーディング規約であり、パフォーマンスや可読性を万人に保証するものではありません。
ただし、できるだけベストプラクティスに近い形になるように心がけています。
初歩的なものばかりになってしまったので、いつか型に関するコーディング規約とTipsに関する第二弾を書きたいと思います。
↓こういう感じのとか。
interface PropsA {
type: "typeA",
content?: never;
};
interface PropsB {
type: "typeB",
content: React.ReactNode;
};
type Props = PropsA | PropsB;
TypeScriptの型システムを最大限活かすことで、Reactの安全に書くことができると思います。
もしも各項目でより良い型付けがあれば是非教えて下さい。