TypeScriptのtypeとinterfaceの違いを整理してみた【ユニオン型も解説】
こんにちは。今日は TypeScript の type と interface の違い、そしてよく一緒に出てくる ユニオン型 についてまとめます。
自分の勉強メモも兼ねて書いているので、これから TypeScript を学ぶ人の参考になれば嬉しいです。
typeとinterfaceの共通点
- どちらも 型を定義するために使える
- オブジェクトの形を表したり、再利用性を高めることができる
一見すると「どっちを使っても同じじゃない?」と思うかもしれませんが、実は特徴が少し違います。
typeの特徴
- ユニオン型やプリミティブ型のエイリアスに使える
&(インターセクション)を使えば型を合成できる- ただし 宣言マージはできない
例:ユニオン型の定義
export type AccountingType = | "収入" | "支出(現金)" | "支出(クレジットカード)";
この場合、AccountingType は 3つの文字列のどれか を意味します。
interfaceの特徴
- 主にオブジェクトの形を表現するのに使う
extendsで他のインターフェースを継承できる- 宣言マージが可能 → 後から同じ名前で再定義すると自動で結合される
例:オブジェクト型の定義
export interface Accounting { id: string; date: string; amount: number; content: string; type: AccountingType; category: IncomeCategory | ExpenseCategory; }
宣言マージの違い
interface の場合
同じ名前で再宣言すると自動で結合されます。
interface Accounting { id: string; amount: number; } // 後から拡張できる! interface Accounting { memo: string; } const a: Accounting = { id: "1", amount: 1000, memo: "昼ごはん代" };
あとから自由にフィールドを追加できるのが大きな特徴。
type の場合
同じ名前で再宣言はできません。
type Accounting = { id: string; amount: number; }; // 再宣言するとエラー // type Accounting = { memo: string }; // 拡張するなら & を使う type AccountingWithMemo = Accounting & { memo: string }; const b: AccountingWithMemo = { id: "2", amount: 2000, memo: "家賃" };
type は その場で閉じた型を作るイメージです。
ユニオン型とは?
ユニオン型は「どれかひとつ」を表す型です。
| を使います。
let value: string | number; value = "こんにちは"; // OK value = 123; // OK value = true; // エラー
今回の例だと、IncomeCategory | ExpenseCategory がまさにユニオン型です。
「収入カテゴリ」か「支出カテゴリ」のどちらか、という意味になります。
まとめ
type
- ユニオン型やプリミティブ型に強い
- 拡張するときは & を使う
interface
- オブジェクトの型を表すのに便利
- 宣言マージがあるので後から拡張できる
ユニオン型
- 「型の選択肢を列挙」できる便利な仕組み
僕の場合、
- 固定的に使う型 → type
- 将来拡張したい型 → interface
という感じで使い分けています。
TypeScript の型は慣れると本当に強力なので、ぜひいろいろ試してみてください。
React × TypeScript × FullCalendar 開発での注意点
先日、React × TypeScript × FullCalendar を使って開発していたときに、以下のようなエラーに遭遇しました。
'DatesSetArg' は型であり、'verbatimModuleSyntax' が有効であるときは、 型のみのインポートを使用してインポートされる必要があります。ts(1484)
最初は「何これ?」と戸惑ったのですが、調べてみると TypeScript 5.0 以降の仕様変更 が関係していました。今回はこのエラーの概要、原因、そして対策方法をまとめます。
エラーが出たコード例
FullCalendar の DatesSetArg 型をインポートして使おうとしたときにエラーが発生しました。
import dayGridPlugin from "@fullcalendar/daygrid"; import FullCalendar from "@fullcalendar/react"; import { DatesSetArg } from '@fullcalendar/core' // ←ここでエラー const Calendar = () => { const handleDateSet = (datesetInfo: DatesSetArg) => { const currentMonth = datesetInfo.view.currentStart } }
エラーの原因
TypeScript 5.0 から導入された verbatimModuleSyntax がポイントです。
これを true にしていると、
- 値(JavaScriptとして存在するもの)
- 型(コンパイル時だけ必要なもの)
を明確に分けてインポートする必要があります。
今回 DatesSetArg は 型だけ なので、普通の import を使うとエラーになってしまいました。
解決方法
解決方法は大きく2つあります。
1. import type を使う
型だけをインポートする場合は import type を使えばOKです。
import type { DatesSetArg } from '@fullcalendar/core'
これでエラーは解消されますし、「これは型だよ」というのが明確になるので、将来的にもおすすめの方法です。
2.
tsconfig.json で verbatimModuleSyntax を無効にするもし「いちいち import type を書くのが面倒」という場合は、tsconfig.json で設定をオフにする方法もあります。
{ "compilerOptions": { "verbatimModuleSyntax": false } }
これで従来通りの挙動に戻せます。
まとめ
- TypeScript 5.0 から 型と値のインポートを区別 する必要がある
- DatesSetArg は「型」なので、普通の import だとエラーになる
- 解決方法は
import typeを使うtsconfig.jsonでverbatimModuleSyntax: falseにする
自分は今回、素直に import type に修正して解決しました。
同じように TypeScript 5.0 以降で「急にエラーが出た!」と困っている方の参考になれば幸いです!
createThemeでデザインルールを一元管理する
フロントエンド開発では、見た目を整えるためにCSSやUIフレームワークを組み合わせて使うことが多いですが、その中でも人気が高いのが MUI (Material UI) です。
MUIは、Googleが提唱する Material Design に基づいたコンポーネントを提供してくれるライブラリで、Reactとの相性も非常に良いのが特徴です。
この記事では、MUIのインストールからテーマ機能である createTheme の役割までを解説します。
MUIのインストール
まずはプロジェクトにMUIを追加します。
npm install @mui/material @emotion/react @emotion/styled
@mui/material: MUIの本体パッケージ@emotion/react / @emotion/styled: MUIが内部で利用しているCSS-in-JSライブラリ
これらをインストールすることで、MUIのコンポーネントをReactで利用できるようになります。
MUIのコンポーネントを使ってみる
インストール後、まずはボタンを表示してみましょう。
import Button from '@mui/material/Button'; function App() { return <Button variant="contained">ADD EVENT</Button>; }

このコードを実行すると、MUI標準のスタイルが適用されたボタンが表示されます。
CSSを一切書かずに、洗練されたUIを利用できるのがMUIの大きな利点です。
createThemeとは?
MUIを使っていると必ず出てくるのが 「テーマ (theme)」 という概念です。
テーマの役割
といった、アプリ全体のデザインルールをひとまとめに管理できます。
このテーマを生成するのが createTheme です。
createThemeの基本例
import { createTheme, ThemeProvider } from '@mui/material/styles'; import { CssBaseline, Button } from '@mui/material'; const theme = createTheme({ palette: { primary: { main: '#1976d2', // プライマリカラー }, secondary: { main: '#9c27b0', // セカンダリカラー }, }, typography: { fontFamily: 'Roboto, Arial, sans-serif', fontSize: 14, }, }); function App() { return ( <ThemeProvider theme={theme}> <CssBaseline /> <Button variant="contained" color="primary">プライマリ</Button> <Button variant="outlined" color="secondary">セカンダリ</Button> </ThemeProvider> ); }

ポイント
さらにカスタマイズ
MUIのテーマは、単に色やフォントを設定するだけでなく、独自のプロパティを追加して拡張 することもできます。
例えば「未着手」「進行中」「完了」といった、アプリ固有の色をテーマに組み込んでおけば、アプリ全体で一貫したカラールールを適用できます。
MUIでは @mui/material/styles を拡張して型定義を追加することで、createTheme の palette に独自のキーを生やせます。
// MUIのテーマ拡張を行うための型定義 declare module "@mui/material/styles" { // theme.palette に独自の色を追加 interface Palette { todoColor: PaletteColor; inProgressColor: PaletteColor; doneColor: PaletteColor; } // createTheme の引数に渡す型も拡張 interface PaletteOptions { todoColor?: PaletteColorOptions; inProgressColor?: PaletteColorOptions; doneColor?: PaletteColorOptions; } } export const theme = createTheme({ typography: { fontFamily: 'Noto Sans JP', fontWeightRegular: 400, fontWeightMedium: 500, fontWeightBold: 700, }, palette: { // 未着手の色 todoColor: { main: blue[500], light: blue[100], dark: blue[700], }, // 進行中の色 inProgressColor: { main: red[500], light: red[100], dark: red[700], }, // 完了の色 doneColor: { main: green[500], light: green[100], dark: green[700], }, }, });
ポイント
declare module "@mui/material/styles"を使ってPalette / PaletteOptionsを拡張すると、TypeScriptで型補完が効くようになるtheme.palette.todoColor.mainのようにアクセス可能になり、アプリ全体で一貫したカラールールを適用できる- ドメインに合わせて「アプリ固有の色」をテーマに登録することで、スタイルの管理がシンプルになる
レスポンシブ対応
テーマに含まれる breakpoints を使うと、画面サイズごとにスタイルを切り替え可能です。
sx={{ fontSize: { xs: '0.8rem', // モバイル sm: '1rem', // タブレット以上 md: '1.2rem', // デスクトップ以上 } }}
まとめ
- MUIを導入すると、Material Designに沿ったUIをすぐに使える
- createTheme は「アプリ全体のデザインルール」をまとめる仕組み
- ThemeProvider でラップすれば、全コンポーネントに統一的なスタイルが反映される
- 色・文字・余白・レスポンシブ・コンポーネントごとのデフォルトを一括で管理可能
デザインを手作業で整えるのは大変ですが、MUIと createTheme を使えば 「開発スピード」と「統一感のあるUI」 を両立できます。
TypeScriptエラー対策:MUI v7 Grid APIの変更に注意
MUIを使って以下のようなコードを書いたところ、TypeScriptでエラーが出ました。
import { Card, CardContent, Grid, Typography } from "@mui/material"; const MonthlySummary = () => { return ( <Grid container spacing={{ xs: 1, sm: 2 }} mb={2}> <Grid xs={4} display="flex" flexDirection="column"> <Card sx={{ color: "white", borderRadius: "10px", flexGrow: 1 }}> <CardContent> <Typography variant="h6">Summary Title</Typography> <Typography variant="body2">Summary content goes here.</Typography> </CardContent> </Card> </Grid> </Grid> ); }; export default MonthlySummary;
出てきたエラーメッセージはこちらです。
この呼び出しに一致するオーバーロードはありません。
...
プロパティ 'component' は型 '{ children: Element; xs: number; display: "flex"; flexDirection: "column"; }' にありませんが、
型 '{ component: ElementType<any, keyof IntrinsicElements>; }' では必須です。
...
プロパティ 'xs' は型に存在しません。
つまり、xs が存在しないとか component が必須と怒られてしまいました。
原因
調べてみると、これは MUI v7 で Grid のAPIが変更されたことが原因でした。
旧Grid(v5まで / v6 Legacy)
- 子要素に
itemを書く必要がある - サイズ指定は
xs={4}などで行う
新Grid(v7以降)
- 子要素は常に
item扱い → item は不要 - サイズ指定は
size={{ xs: 4 }}またはsize={4}に変更
今回、自分は import { Grid } from "@mui/material"; で 新しいGrid を読み込んでいるのに、書き方を旧Gridのままにしていたため、型エラーが出ていました。
解決方法
エラーを直す方法は2通りあります。
1. 新しいGridに合わせる
<Grid container spacing={{ xs: 1, sm: 2 }} sx={{ mb: 2 }}> <Grid size={{ xs: 4 }} display="flex" flexDirection="column"> ... </Grid> </Grid>
ポイント
- 子は
itemを書かない xsではなくsizeを使う
2. 旧Grid(GridLegacy)を使う
もしこれまで通り xs={4} や item を使いたいなら、GridLegacy をインポートします。
import Grid from "@mui/material/GridLegacy"; <Grid container spacing={2} sx={{ mb: 2 }}> <Grid item xs={4}> ... </Grid> </Grid>
Reactコンポーネントの書き方とexport方法の違い
Reactコンポーネントの書き方にはいくつかパターンがあります。
特に 関数の書き方 と export方法(default / named) の違いは、最初は分かりにくいところです。
この記事では、
- default export(後出し)の意味と使いどころ
- 巻き上げ(hoisting)とは何か
- 関数の書き方+exportの違いと使い分け
をわかりやすくまとめます。
1. export の種類
default export(デフォルトエクスポート)
- 1ファイルにつき1つだけ
- import時に好きな名前を付けられる
export default function App() { return <div>App</div> } // import例 import MyApp from './App'
named export(名前付きエクスポート)
- 1ファイルにいくつでも書ける
- import時は名前を合わせる必要がある
export const Home = () => <h1>Home</h1> export const About = () => <h1>About</h1> // import例 import { Home, About } from './pages/Home'
2. 関数の書き方の2種類
関数宣言(function declaration)
function App() { return <div>App</div> }
巻き上げ(hoisting) がされる
→ ファイル内で宣言より前に呼び出しても使える
関数式(アロー関数)
const Home = () => { return <div>Home</div> }
巻き上げされない
→ 宣言より前で呼び出すとエラーになる
3. 巻き上げ(hoisting)とは?
JavaScriptでは、関数宣言や変数宣言が実行前にファイルの先頭に持ち上がったように扱われる仕組みがあります。
例(関数宣言はOK):
sayHello() // ✅ 動く function sayHello() { console.log("Hello") }
例(アロー関数はNG):
sayHello() // ❌ エラー const sayHello = () => { console.log("Hello") }
4. default export 後出し とは?
関数を先に宣言し、最後に export default する書き方です。
// 先に変数や関数などの準備 const appTitle = "My Cool App" function App() { return <h1>{appTitle}</h1> } // 最後にdefault export export default App
この書き方のメリット:
- コンポーネントの前に変数や補助関数を置ける
- 同じファイル内で複数の関数を定義し、最後に主役だけexportできる
ReactでSPA構築: react-router-domの基本的な使い方
ReactでSPA(シングルページアプリケーション)を作るとき、ほぼ必須になるのがreact-router-domです。
今回は基本的な使い方から、AppLayoutを使った共通レイアウトの作成方法までまとめていきます。
1. react-router-domとは?
react-router-dom は React用のルーティングライブラリ です。
「ルーティング」とは、URLに応じてどのコンポーネントを表示するかを切り替える仕組みのこと。
Reactは単一ページ(SPA)なので、ページ遷移に見えても実際はコンポーネントの切り替えで画面を更新しています。その切り替えを管理してくれるのが react-router-dom です。
2.インストール方法
npm i react-router-dom
3.よく使うコンポーネント・フック
| コンポーネント/フック | 役割 |
|---|---|
BrowserRouter |
履歴管理付きのルーター。普通のWebアプリはこれを使う |
Routes |
ルート定義をまとめる |
Route |
URLパスとコンポーネントを紐付け |
Link |
アプリ内リンク(<a>タグ代わり) |
Outlet |
ネストされたルートの表示位置 |
Navigate |
コードからページ遷移(リダイレクト) |
useParams |
URLパラメータ取得 |
useNavigate |
ページ遷移用の関数取得 |
4.ルーティング例
<Route index>は「親ルートにアクセスしたときのデフォルトの子ルート」を設定するための属性です。
上記の例では
/にアクセスすると自動的に<Home />が表示される/reportにアクセスすると<Report />が表示される
という動きになります。
5.共通レイアウトを作るAppLayout
ルーティング例を見ていただくと、AppLayoutコンポーネントが親ルートとしてあり、Homeコンポーネント、ReportコンポーネントがAppLayoutコンポーネントの子ルートとして配置されています。
なぜAppLayoutを用意するのか?
- ヘッダーやフッターなど全ページ共通UIをまとめて管理できる
<Outlet />で子ルートの表示位置を制御できる- 認証チェックやエラーハンドリングを一括で適用できる
コードの動き
<Outlet />の場所に子ルートのコンポーネントが差し込まれる/→<Home />、/report→<Report />が表示される- AppLayoutコンポーネントにヘッダーやナビゲーションを書けば常に共通で表示される
6.シングルページアプリケーション(SPA)とは
Single Page Applicationの略で、最初に読み込むHTMLは1枚だけで、ページ遷移はJavaScriptで中身だけ差し替える仕組みのWebアプリのことです。
MPA(Multi Page Application)との違い
| 特徴 | SPA | MPA |
|---|---|---|
| HTML読み込み | 最初の1回のみ | ページごとに読み込み |
| ページ切り替え | JSで中身だけ切り替え | ページ全体を再読み込み |
| 表示速度 | 高速 | 遅め |
| SEO | 対策が必要 | 標準で有利 |

Reactでページ切り替えを行うには、react-router-domなどのルーターライブラリを用いることで実現します。
7.まとめ
react-router-domはReactでのページ切り替えを管理するライブラリBrowserRouter、Routes、Routeが基本- 共通UIはAppLayoutにまとめ、
<Outlet />で子ルートを挿入 Route indexは親ルートのデフォルト表示を設定できる
Reactでアプリを作るとき、ルーターを正しく理解しておくと画面設計がグッと楽になりそうです。
Vite+TypeScriptでのReactプロジェクト作成手順
最近、Reactの勉強を行っています。
このブログでは、学んだことを随時まとめていこうと思います。
今回は「Reactの新しいプロジェクトをVite+TypeScriptで始める方法」について紹介します。
1. React新規プロジェクトをVite+TypeScriptで作成するコマンド
まず、下記コマンドをターミナルで実行します。
npm create vite@latest my-react-app -- --template react-ts
-
my-react-appは好きなプロジェクト名でOK -
--template react-tsで「React + TypeScript」構成になる
続けて、以下のコマンドも実行します。
cd my-react-app
npm install
npm run dev
これらのコマンドはターミナルにも表示されるので安心ですね。

npm run devコマンドを実行するとターミナルに開発サーバーのURL(http://localhost:5173)が表示されます。

このURLをブラウザで開くと、Reactアプリが立ち上がります。
初期画面はこんな感じです。

Reactプロジェクトの作成は、とても簡単です。
2. npmとnpxの違い
ここでよく登場する「npm」や「npx」についても整理しておきます。
npmとは
-
Node.jsのパッケージ管理ツール。プロジェクトに必要なライブラリのインストールなどを担当。
-
例:
npm install react
npxとは
-
npmパッケージを一時的に実行するツール。
-
インストールされていないパッケージでも自動的に探して、ローカルに一時的にインストールし実行。
3. npm createとnpx createの違い・使い分け
-
npm createは npm v6以降で使える新しい標準コマンド -
npx create-xxxとほぼ同じ働きだが、npm createが今後の主流 -
npmのバージョンはnpm -vで確認できる。6以上ならOK
調べると、上記のような違いがあるようです。
npm公式ドキュメントでもnpm createを推奨しているので、npm createコマンドを使用しておけば間違いなさそうです。
※npm createはnpm initのエイリアスとして動作
4. Viteって何?
-
Vite(ヴィート)は「超高速な開発サーバー&ビルドツール」
-
React/Vue/SvelteなどのモダンなWeb開発で、今もっともよく使われている
-
create-react-appやwebpackよりもシンプル&高速
とてもいいなと思ったのは、開発サーバーがとにかく速いこと!ソースを修正すると、ブラウザに即反映されます。
最初に紹介した3つのコマンドで、即座にReactアプリが立ち上がるのは本当に便利です。
npm create vite@latest my-react-app -- --template react-tsnpm install
npm run dev
5. npm installの意味
npm createの実行直後は、まだパッケージはダウンロードされていません。npm createは、package.jsonや初期ファイル一式を自動生成するだけです。npm installコマンドを実行することで、package.jsonの内容に従い必要なパッケージが「node_modules」フォルダにインストールされるとのことです。
6. まとめ
Vite + TypeScriptを使ったReactプロジェクトの作成は、コマンド数も少なくスピーディーでとてもおすすめです。
npmやnpxの違い、npm installの役割も押さえておくと、今後の開発がスムーズになりそうです。