メインコンテンツへスキップ
Header
このドキュメントには、コードを書く際に従うべきルールが含まれています。 ここでの目標は、一貫性があり、読みやすく、メンテナンスしやすいコードベースにすることです。 そのためには、簡潔すぎるよりも少し冗長な方が良いです。 常に念頭に置いておくべきは、コードは書くより読む方が多いということ、特にオープンソースプロジェクトでは、誰でも貢献できるためです。 ここでは定義されていない、多くの規則がありますが、リンターにより自動的にチェックされます。

React

関数コンポーネントを使用する

常にTSXの関数コンポーネントを使用してください。 constを使ったデフォルトのimportは避けてください。読むことや、コード補完でのインポートが難しくなるからです。
// ❌ Bad, harder to read, harder to import with code completion
const MyComponent = () => {
  return <div>Hello World</div>;
};

export default MyComponent;

// ✅ Good, easy to read, easy to import with code completion
export function MyComponent() {
  return <div>Hello World</div>;
};

プロパティ

プロップスの型を作成し、エクスポートの必要がない場合にはそれを(ComponentName)Propsと呼んでください。 Use props destructuring.
// ❌ Bad, no type
export const MyComponent = (props) => <div>Hello {props.name}</div>;

// ✅ Good, type
type MyComponentProps = {
  name: string;
};

export const MyComponent = ({ name }: MyComponentProps) => <div>Hello {name}</div>;

プロップス型の定義にReact.FCまたはReact.FunctionComponentを使用しない

/* ❌ - Bad, defines the component type annotations with `FC`
 *    - With `React.FC`, the component implicitly accepts a `children` prop
 *      even if it's not defined in the prop type. This might not always be
 *      desirable, especially if the component doesn't intend to render
 *      children.
 */
const EmailField: React.FC<{
  value: string;
}> = ({ value }) => <TextInput value={value} disabled fullWidth />;
/* ✅ - Good, a separate type (OwnProps) is explicitly defined for the 
 *      component's props
 *    - This method doesn't automatically include the children prop. If
 *      you want to include it, you have to specify it in OwnProps.
 */ 
type EmailFieldProps = {
  value: string;
};

const EmailField = ({ value }: EmailFieldProps) => (
  <TextInput value={value} disabled fullWidth />
);

JSX要素内で単一の変数プロップスプレーディングを使用しない

{...props}のようにJSX要素内で単一の変数プロップスプレーディングを避けてください。 この方法は、コンポーネントが受け取るプロップスを不明確にするため、コードの可読性が低下し、メンテナンスが困難になります。
/* ❌ - Bad, spreads a single variable prop into the underlying component
 */
const MyComponent = (props: OwnProps) => {
  return <OtherComponent {...props} />;
}
/* ✅ - Good, Explicitly lists all props
 *    - Enhances readability and maintainability
 */ 
const MyComponent = ({ prop1, prop2, prop3 }: MyComponentProps) => {
  return <OtherComponent {...{ prop1, prop2, prop3 }} />;
};
根拠:
  • ひと目で、コードがどのプロップスを渡しているかが明確になり、理解とメンテナンスが容易になります。
  • これにより、コンポーネント間のプロップスによる密結合を防ぐのに役立ちます。
  • プロップスを明示的にリストすれば、lintingツールが綴り間違いや未使用のプロップスを特定しやすくなります。

JavaScript

??(ヌリッシュ合体演算子)を使う

// ❌ Bad, can return 'default' even if value is 0 or ''
const value = process.env.MY_VALUE || 'default';

// ✅ Good, will return 'default' only if value is null or undefined
const value = process.env.MY_VALUE ?? 'default';

?.(オプショナルチェイニング)を使う

// ❌ Bad 
onClick && onClick();

// ✅ Good
onClick?.();

TypeScript

typeを使い、常にinterfaceの代わりにする

typeは多くの場合、interfaceと重複し、typeの方が柔軟性が高いため、常にtypeを使用してください。
// ❌ Bad
interface MyInterface {
  name: string;
}

// ✅ Good
type MyType = {
  name: string;
};

列挙型の代わりに文字列リテラルを使う

文字列リテラル は、TypeScriptで列挙型のような値を扱うための推奨方法です。 それらはPickやOmitで拡張するのが簡単で、特にコード補完を伴う開発者の体験を向上させます。 なぜTypeScriptが列挙型を避けることを推奨しているかはここをご覧ください。
// ❌ Bad, utilizes an enum
enum Color {
  Red = "red",
  Green = "green",
  Blue = "blue",
}

let color = Color.Red;
// ✅ Good, utilizes a string literal

let color: "red" | "green" | "blue" = "red";

GraphQLと内部ライブラリ

GraphQLのcodegenが生成する列挙型を使用する必要があります。 内部ライブラリを使用する際にも列挙型を使用する方が良いので、内部ライブラリが内部APIに関連しない文字列リテラル型を公開する必要がなくなります。 例:
const {
  setHotkeyScopeAndMemorizePreviousScope,
  goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();

setHotkeyScopeAndMemorizePreviousScope(
  RelationPickerHotkeyScope.RelationPicker,
);

スタイリング

StyledComponentsを使用する

コンポーネントをstyled-componentsでスタイル設定する。
// ❌ Bad
<div className="my-class">Hello World</div>
// ✅ Good
const StyledTitle = styled.div`
  color: red;
`;
スタイル付きコンポーネントを”Styled”で接頭辞にして、“本物の”コンポーネントと区別する。
// ❌ Bad
const Title = styled.div`
  color: red;
`;
// ✅ Good
const StyledTitle = styled.div`
  color: red;
`;

テーマ

大部分のコンポーネントのスタイルをテーマに基づかせるのが推奨される手法です。

測定の単位

Styledコンポーネント内でpxまたはrem値を直接使用しない 必要な値は一般的にテーマに既に定義されているため、これらの目的でテーマを利用することが推奨されます。

カラー

新しい色を追加するのではなく、テーマから既存のパレットを使用してください。 パレットが適合しない場合には、コメントを残してチームがそれを修正できるようにしてください。
// ❌ Bad, directly specifies style values without utilizing the theme
const StyledButton = styled.button`
  color: #333333;
  font-size: 1rem;
  font-weight: 400;
  margin-left: 4px;
  border-radius: 50px;
`;
// ✅ Good, utilizes the theme
const StyledButton = styled.button`
  color: ${({ theme }) => theme.font.color.primary};
  font-size: ${({ theme }) => theme.font.size.md};
  font-weight: ${({ theme }) => theme.font.weight.regular};
  margin-left: ${({ theme }) => theme.spacing(1)};
  border-radius:  ${({ theme }) => theme.border.rounded};
`;

タイプ無しのインポートの強制

タイプインポートを避けてください。 この標準を強制するために、ESLintルールがどのタイプのインポートもチェックして報告します。 これにより、TypeScriptコードの一貫性と可読性が維持されます。
// ❌ Bad
import { type Meta, type StoryObj } from '@storybook/react';

// ❌ Bad
import type { Meta, StoryObj } from '@storybook/react';

// ✅ Good
import { Meta, StoryObj } from '@storybook/react';

なぜタイプなしインポートなのか

  • 一貫性: 型インポートを避け、型と値の両方に単一のアプローチを使用することで、モジュールインポートスタイルの一貫性が保たれます。
  • Readability: No-type imports improve code readability by making it clear when you’re importing values or types. This reduces ambiguity and makes it easier to understand the purpose of imported symbols.
  • 維持性: コードベースの維持性を向上させます。開発者がコードを確認/変更する際に、タイプのみのインポートを特定して見つけることができるからです。

ESLintルール

An ESLint rule, @typescript-eslint/consistent-type-imports, enforces the no-type import standard. このルールは、タイプインポート違反のエラーや警告を生成します。 Please note that this rule specifically addresses rare edge cases where unintentional type imports occur. TypeScript自体、TypeScript 3.8 リリースノートでこのプラクティスを避けています。 ほとんどの状況で、タイプのみのインポートを使用する必要はありません。 To ensure your code complies with this rule, make sure to run ESLint as part of your development workflow.