typescript-dev について
typescript-devは、Vite、React、Tailwind CSS、Biome、Vitest、Honoを組み合わせたTypeScriptフロントエンド開発スキルです。ツール間の注意点を扱い、エンドツーエンドで型安全なアプリの構成例を示します。
主な機能
- 01フロントエンドとバックエンドのツール構成
- 02ViteとReact Compilerの設定
- 03Tailwind CSS v4とshadcn/uiの利用
- 04Biomeによるフォーマットとチェック
- 05Vitestによるテスト作成
- 06Hono RPCによるフロントとバックの型共有
活用シーン
- 01ReactとTypeScriptアプリの構築
- 02Viteのプラグイン順序やHMRの問題解決
- 03Tailwind CSS v4のスタイルとテーマ設定
- 04Hono APIとReactクライアントの接続
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tenequm/ skills --skill typescript-devインストール後、Claude Code で/typescript-devを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CHANGELOG.mdLICENSE.txtreferences/biome.mdhono.mdreact.mdshadcn.mdtailwind.mdtypescript.mdvite.mdvitest.md詳細
TypeScript フロントエンド開発
型安全な TypeScript アプリを構築するための一貫したスタックです。Vite 8(ビルドと開発サーバー、Rolldown 搭載)、React 19.3(React Compiler 搭載)、TypeScript 7.0(strict、Go ネイティブ)、スタイリング用の Tailwind CSS v4.3 + shadcn/ui、リンティングとフォーマット用の Biome 2.5、テスト用の Vitest 5、バックエンド/エッジ API 用の Hono 4 で構成されています。各要素は相互に連携するよう設計されています。このスキルでは、それらの接続方法と、複数の要素にまたがる注意点を解説します。Hono の RPC クライアント(hc)はサーバーの型を React フロントエンドと直接共有するため、コード生成なしでフロントエンドからバックエンドまで一貫して型安全性を保てます。
以下の本文では、これらのツールを組み合わせたときに問題になりやすいルールと、動作するエンドツーエンドのセットアップを扱います。各ツールには詳しいリファレンスもあります。必要なものをお読みください。
- references/vite.md - Vite 8 の設定、開発サーバー、プロキシ、HMR、Rolldown、コード分割、ビルドの最適化、デプロイ。
- references/react.md - React 19 のパターン:Actions、
use()、Activity、<ViewTransition>、Fragment refs、useEffectEvent、ドキュメントメタデータ、React Compiler。 - references/typescript.md - 厳格な TypeScript 7.0 の設定とパターン:tsconfig のデフォルト、API を使用するツール向けの 6.0 互換パス、ジェネリクス、
import defer。 - references/tailwind.md - Tailwind CSS v4 の CSS ファースト設定、OKLCH テーマ、ダークモード、v4.3 のユーティリティ。
- references/shadcn.md - shadcn/ui CLI、CVA +
data-slotを使ったコンポーネントの作成、cnパッケージ、レジストリ、Base UI / Radix / React Aria。 - references/biome.md - Biome の設定、
biome check、ドメイン、型を考慮したリンティング、GritQL、ESLint/Prettier からの移行。 - references/vitest.md - Vitest 5 の設定、Testing Library、モック、カバレッジ、ブラウザモード、プロジェクト、v4 から v5 への移行、テスト速度。
- references/hono.md - Hono 4 ウェブフレームワーク:ルーティング、コンテキスト、ミドルウェア、バリデーション(Zod)、エンドツーエンドの型安全な RPC、OpenAPI、ヘルパー、複数のランタイムへのデプロイ(Workers/Node/Bun/Deno)。
バージョンの対象
| ツール | バージョン | 備考 |
|---|---|---|
| Vite | 8.3.3 | Rolldown が唯一のデフォルトバンドラーです |
| @vitejs/plugin-react | 6.1.2 | v6 ではインラインの babel オプションが削除されました |
| React / react-dom | 19.3.0 | React Compiler は安定版(1.0)です |
| babel-plugin-react-compiler | 1.0.0 | --save-exact で固定してください |
| TypeScript | 7.0.2 | Go ネイティブで約 10 倍高速です。JS API は付属しません(typescript.md に 6.0 互換パスがあります) |
| Tailwind CSS | 4.3.3 | CSS ファースト設定で、JS 設定ファイルはありません |
| shadcn/ui CLI | 4.21.3 | Base UI がデフォルトのベースです。cn は独立したパッケージです |
| Biome | 2.5.15 | リント、フォーマット、インポートに対応する単一バイナリです |
| Vitest | 5.0.3 | Vite ネイティブのテストランナーで、vite.config を再利用します |
| Hono | 4.13.13 | Web Standards 対応のバックエンド/エッジフレームワークです。v5 は開発中です |
スタック全体で実質的な最低要件となるのは Node.js 22.12+ です。Vite 単体なら 20.19+ に対応しますが、Vitest 5 と @rolldown/plugin-babel はどちらも 22.12+ を必要とします。
ツールをまたぐ重要なルール
これらは、複数のツールの境界に関わるため、分かりにくい形で失敗するルールです。個々のツールに関する詳細は各リファレンスに記載されています。
Vite プラグインの順序:フレームワークプラグインを先に、react() を最後に
フレームワークプラグイン(TanStack Router/Start など)がルートを生成したりコードを変換したりする場合は、@vitejs/plugin-react より先に実行する必要があります。こうすることで、React の Fast Refresh 変換が最終的な出力を認識できます。順序が間違っていると、ルート生成の失敗や HMR の不具合につながります。
plugins: [
tanstackStart(), // or tanstackRouter() for SPA - framework first
tailwindcss(),
react(), // React plugin last among framework plugins
]
React Compilerは手動のメモ化を置き換え、Viteの配線方法を変えます
React Compiler 1.0は、ビルド時にコンポーネント、計算処理、コールバックを自動でメモ化します。通常のコンポーネントを書き、useMemo/useCallback/memoは使わないでください。注意すべき点はViteとの接続部分です。@vitejs/plugin-react v6ではインラインのbabelオプションが削除されたため、以前のreact({ babel: { plugins: [...] } })という配線方法は機能しません。現在、コンパイラーは独立したBabelプラグインを介して実行します。
import react, { reactCompilerPreset } from '@vitejs/plugin-react'
import babel from '@rolldown/plugin-babel'
plugins: [react(), babel({ presets: [reactCompilerPreset()] })]
インストール:pnpm add -D @rolldown/plugin-babel @babel/core babel-plugin-react-compiler @types/babel__core。
plugin-react 6.1には、Babelを完全にスキップする実験的なネイティブ(Rust)コンパイラーパスreact({ compiler: true })もあります。インストール時の注意点については、react.mdを参照してください。上記のBabelによる配線方法が、引き続き安定した方法です。
この変更はBiomeにも影響します。useExhaustiveDependenciesは依存関係をコンパイラーが処理していることを認識できないため、コンパイラーを使う場合はほとんどの場合、このルールを無効にします(biome.mdを参照)。
Tailwind v4はCSSファーストです。tailwind.config.jsはありません
Tailwind v4では、@theme、@utility、@plugin、@sourceを使って、すべてをCSSで設定します。tailwind.config.js/.tsを作成したり、探したりしないでください。Viteとの統合には@tailwindcss/viteプラグインを使います(PostCSS設定もありません)。v4のプロジェクトでtailwind.config.jsが見つかった場合は、古いファイルです。削除して、設定値をCSSに移行してください。詳しくはtailwind.mdを参照してください。
セマンティックトークンでスタイルを指定し、生のカラーパレットや動的なクラス名は使わないでください
<div className="bg-primary text-primary-foreground"> // respects theme + dark mode
<div className="bg-blue-500 text-white"> // breaks theming - avoid
また、クラス名を断片から組み立てないでください(bg-${color}-500)。Tailwindのスキャナーが認識するのは完全な文字列リテラルだけなので、動的な名前ではCSSが生成されず、エラーも表示されません。完全なクラス文字列を使ったルックアップマップを使用してください。
TypeScript 7.0:新しいデフォルト設定を活用し、JavaScript APIがないことを把握してください
TS 6.0と7.0では、これまで手動で設定していた項目の多くが組み込まれています。strictとnoUncheckedSideEffectImportsはデフォルトで有効なので、新規のtsconfigからは削除してください。2つのデフォルト設定は、無視するとViteアプリで問題を引き起こします。typesのデフォルト値は[]で、副作用のあるインポートもチェックされるため、"types": ["vite/client"]を追加するまではimport "./styles.css"が失敗し(TS2882)、import.meta.envも型付けされません(TS2339)。必要に応じて"node"などを追加してください。baseUrlは非推奨です。プレフィックス付きのpathsを使用してください。
通常のpnpm add -D typescriptでは、tscバイナリは含まれるもののプログラムから使えるAPIは含まれない7.0がインストールされます(APIが戻るのは7.1です)。このスタックでは問題ありません。Biome、plugin-react、shadcn CLIはtypescriptをインポートしないためです。ただし、typescript-eslint、フレームワークチェッカー(Vue、Astro、Svelte、MDX)、およびimport ts from "typescript"を実行するその他のツールには、公式の6.0エイリアスのペアが必要です。typescript.mdを参照してください。
Biomeのコマンドは1つだけ。インクルードキーはfiles.includesのみです
biome check(またはbiome ci)を実行してください。フォーマット、リント、インポートの整理を1回で行えます。lintとformatを別々に実行しないでください。また、Biome 2.xでファイル選択に使えるキーはfiles.includes(末尾にs)のみです。files.ignore/files.include/files.excludeは存在せず、Found an unknown keyというエラーが発生します。除外するには否定を使います。"includes": ["**", "!**/routeTree.gen.ts"]。biome.mdを参照してください。
Hono RPC はバックエンドの型を React フロントエンドに結び付け、同期を保ちます
API に Hono を使う場合、React アプリはサーバーがエクスポートした typeof app を直接インポートする hc<AppType>() クライアントを通じて API とやり取りします。この共有型が接点となります。正常に機能するには、両側で同じ Hono バージョンを実行し、両方の tsconfig.json で "strict": true を設定する必要があります(一致しないと「Type instantiation is excessively deep」エラーが発生します)。この接点では、さらに次の 2 つのルールにも注意が必要です。クライアントがレスポンスを推論できるよう、ハンドラーでステータスコードを指定する必要があります(c.json(data, 200))。また、クライアントが呼び出すルートでは c.notFound() を使わないでください。ルート数が増えてきたら、クライアントの型を一度だけコンパイルする(hcWithType)ことで、IDE の動作を速く保てます。pnpm why hono を実行して、アダプターのピア依存関係の範囲によって 2 つ目のコピーが取り込まれていないか確認してください。Vite 開発サーバーの背後で Hono が CORS を処理する場合は、2 つの CORS レイヤーが競合しないように vite.config.ts で server.cors: false を設定してください。詳細は hono.md を参照してください。
エンドツーエンドのセットアップ
React + TypeScript + Tailwind + Biome を使った、最小限ながら完全なプロジェクトです。フレームワークプラグインは、使用するルーター/SSR の選択に合わせて置き換えてください(TanStack と Cloudflare の構成については vite.md を参照してください)。
vite.config.ts
import { defineConfig } from 'vite'
import react, { reactCompilerPreset } from '@vitejs/plugin-react'
import babel from '@rolldown/plugin-babel'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [
tailwindcss(),
react(),
babel({ presets: [reactCompilerPreset()] }),
],
resolve: {
alias: { '@': new URL('./src', import.meta.url).pathname },
},
})
import.meta.url はパス解決に適した ESM 準拠の方法です。ESM 構成ファイルには __dirname がなく、Vite の構成ファイルは ESM 専用です。
tsconfig.json(TypeScript 7.0。6.0 でも有効)
{
"compilerOptions": {
// strict + noUncheckedSideEffectImports are ON by default since 6.0 - omitted on purpose
"target": "es2023",
"module": "preserve",
"moduleResolution": "bundler",
"moduleDetection": "force",
"jsx": "react-jsx",
"verbatimModuleSyntax": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"erasableSyntaxOnly": true,
"skipLibCheck": true,
"noEmit": true,
"types": ["vite/client"],
"paths": { "@/*": ["./src/*"] }
},
"include": ["src"]
}
Vite でバンドルするアプリには、module: preserve と moduleResolution: bundler の組み合わせが適切です。Node.js で実行するコードの場合のみ、代わりに nodenext を使用してください。"types": ["vite/client"] は必須です。これにより CSS/アセットのモジュールと import.meta.env が宣言されます。これがないと、デフォルトで有効になっている副作用インポートのチェックにより import "./styles.css" が拒否されます。その他のアンビエント型は、"node" や "vitest/globals" のように指定するまで読み込まれません。exactOptionalPropertyTypes を有効にすると、undefined を受け取る可能性があるオプショナルなプロパティは prop?: T | undefined と宣言する必要があります。
biome.json
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
"files": { "includes": ["**", "!**/components/ui", "!**/routeTree.gen.ts"] },
"formatter": { "enabled": true, "indentStyle": "space", "lineWidth": 100 },
"linter": {
"enabled": true,
"rules": { "preset": "recommended" },
"domains": { "react": "recommended" }
},
"javascript": { "formatter": { "quoteStyle": "double" } },
"assist": { "enabled": true, "actions": { "source": { "organizeImports": "on" } } }
}
src/styles.css
@import "tailwindcss";
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--radius: 0.5rem;
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
}
@import "tailwindcss"; の行は必須です。これがないと、@tailwindcss/vite プラグインだけではスタイルが生成されません。インポートの記述漏れは、「Tailwind が何も描画しない」という典型的な落とし穴です。CSS 変数を参照するトークンには、通常の @theme ではなく @theme inline を使ってください。そうすることで、ダークモードの変更が反映されます。
スタック全体が想定するコンポーネントの書き方
通常の関数を使い、ref は通常のプロパティとして扱い(forwardRef は不要)、ネイティブ要素のプロパティには React.ComponentProps を使い、バリアントには CVA を使います。スタイル設定用フックには data-slot を使用し、手動のメモ化は行いません。メモ化はコンパイラーが処理します。
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
outline: "border border-input bg-background hover:bg-accent",
ghost: "hover:bg-accent hover:text-accent-foreground",
},
size: { default: "h-9 px-4 py-2", sm: "h-8 px-3", lg: "h-10 px-8" },
},
defaultVariants: { variant: "default", size: "default" },
}
)
function Button({
className,
variant,
size,
ref,
...props
}: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants>) {
return (
<button
ref={ref}
data-slot="button"
className={cn(buttonVariants({ variant, size }), className)}
{...props}
/>
)
}
cn() の順序に注意してください。デフォルト値を先に、利用側の className を最後に指定します。これにより、tailwind-merge の後勝ちの解決によって、呼び出し側が値を上書きできます。
ベストプラクティス
- コンパイラーの最適化に任せます。 通常のコンポーネントや計算を記述し、
useMemo/useCallbackは、値を安定したエフェクト依存関係にする必要があるまれなケースに限って使います。 - 状態は、ばらばらなブール値ではなく判別可能なユニオンとしてモデル化します(
{ status: "loading" } | { status: "error"; error })。そうすることで、あり得ない状態を表現できなくなります。 - HTML 属性を手作業で再宣言するのではなく、
React.ComponentProps<"el">でネイティブのプロパティを拡張します。 useContext()よりuse()を使います。早期リターンの後や条件分岐内でも機能します。- セマンティックなカラーのトークンのみを使い、
bg-*には常に対応するtext-*-foregroundを組み合わせます。 biome check --writeをローカルで実行する唯一のコマンドとし、パイプラインではbiome ciを使います。- Vite 8 では Rolldown がデフォルトのバンドラーです。オプトインは不要です。安定したベンダーコードの分割には Rolldown の
codeSplittingを使います(vite.md を参照)。 - コードを書き換えるツールのバージョンは厳密に固定します(
babel-plugin-react-compiler、@biomejs/biome)。リリース間で予期しない差分が生じるのを防げます。 - シークレットをクライアント側に置かないでください。ブラウザーのコードから
import.meta.env経由で参照できるのは、VITE_を接頭辞に持つ環境変数のみです。 vite.config.ts経由でテストします。Vitest はビルド設定を再利用するため、テストにも同じエイリアスと変換が適用されます。CI ではvitest runを使い、コンポーネントテストにはjsdomを使います。
リソース
- Vite:https://vite.dev/guide/ - Vite 8 のブログ:https://vite.dev/blog/announcing-vite8
- React 19.3:https://react.dev/blog/2026/09/09/react-19-3 - コンパイラー:https://react.dev/learn/react-compiler
- TypeScript 7.0:https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/
- Vitest 5:https://vitest.dev/blog/vitest-5 - Hono:https://hono.dev/docs/
- Tailwind CSS:https://tailwindcss.com/docs - shadcn/ui:https://ui.shadcn.com/docs
- Biome:https://biomejs.dev/