第10章
モジュールと型のインポート/エクスポート
約5分
この章の目次開く
TypeScript/JavaScriptのモジュールシステムの基本と、TypeScript固有の型のインポート・エクスポートを扱います。
学習者import type って普通の import と何が違うの?わざわざ使い分ける意味あるの?
ESModulesの基本
名前付きエクスポートとデフォルトエクスポート
// 名前付きエクスポート(1ファイルに複数OK)
export function formatDate(date: Date): string {
return date.toLocaleDateString('ja-JP');
}
export const MAX_RETRY = 3;
export type User = {
id: string;
name: string;
};tsx
// デフォルトエクスポート(1ファイルに1つ)
export default function Button({ label }: { label: string }) {
return <button>{label}</button>;
}tsx
インポート
// 名前付きインポート
import { formatDate, MAX_RETRY } from './utils';
import { User } from './types';
// デフォルトインポート(好きな名前で受けられる)
import Button from './Button';
// 名前付き + デフォルトを同時に
import Button, { type ButtonProps } from './Button';
// 別名をつける
import { formatDate as formatJaDate } from './utils';tsx
| エクスポート方式 | メリット | デメリット |
|---|---|---|
| 名前付き | 自動補完が効く、rename可、tree-shaking対象 | なし |
| デフォルト | import時に好きな名前をつけられる | 自動補完が効きにくい、名前がバラつく |
import type — 型だけをインポートする
import type を使うと、コンパイル後のJavaScriptからインポート文が完全に除去されます。
// 値と型のインポートを分ける
import { fetchUser } from './api';
import type { User } from './types';
// inline type import(1行にまとめる書き方)
import { fetchUser, type User } from './api';tsx
なぜ import type を使うのか
// ❌ import type を使わない場合
import { User } from './types';
// コンパイル後: import { User } from './types';
// → 実行時に ./types を読み込もうとする(中身は空なのに)
// ✅ import type を使う場合
import type { User } from './types';
// コンパイル後: (この行は消える)
// → バンドルサイズが小さくなるtsx
先生ESLintの @typescript-eslint/consistent-type-imports ルールを有効にすると、型のみのインポートを import type に自動修正してくれるよ。チームで統一するならこのルールがおすすめ。
型と値の名前空間
TypeScriptでは、型と値が別の名前空間に存在します。そのため、同じ名前で型と値を定義できます。
// class は型としても値としても使える
class User {
constructor(public name: string) {}
}
const user: User = new User('Alice'); // User は型でもあり、コンストラクタ(値)でもあるtsx
// enum も型と値の両方の名前空間を持つ
enum Status {
Active = 'active',
Inactive = 'inactive',
}
const s: Status = Status.Active; // 型としても値としても使えるtsx
// type は型の名前空間のみ
type Color = 'red' | 'blue';
// const c = Color; // ❌ エラー: Color は型であり、値として使えないtsx
| キーワード | 型として | 値として |
|---|---|---|
type | ✅ | ❌ |
interface | ✅ | ❌ |
class | ✅ | ✅ |
enum | ✅ | ✅ |
const / let | ❌ | ✅ |
function | ❌ | ✅ |
バレルファイル(index.ts)
関連する型やモジュールを1箇所から再エクスポートするパターンです。
// types/user.ts
export type User = { id: string; name: string; email: string };
export type CreateUserInput = { name: string; email: string };
// types/product.ts
export type Product = { id: string; title: string; price: number };
// types/index.ts(バレルファイル)
export type { User, CreateUserInput } from './user';
export type { Product } from './product';tsx
// 使う側 — 1箇所からまとめてインポートできる
import type { User, Product } from '@/types';tsx
パスエイリアス
tsconfig.json の paths 設定で、深いディレクトリ構造のインポートを短くできます。
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}json
// ❌ 相対パスが深い
import { Button } from '../../../components/ui/Button';
import type { User } from '../../../types/user';
// ✅ パスエイリアスですっきり
import { Button } from '@/components/ui/Button';
import type { User } from '@/types/user';tsx
ちゃんと使うためのポイント
-
import typeを使うと、コンパイル後のJSからインポート文が消え、バンドルサイズが最適化される - 名前付きエクスポートが主流。自動補完とtree-shakingに有利
- 型と値は別の名前空間。
classとenumは両方に存在する - バレルファイルはインポートを楽にするが、tree-shaking への影響に注意
- パスエイリアスで深い相対パスを避ける
次の章では、TypeScriptで既存のJavaScriptライブラリを型安全に使うための**型定義ファイル(.d.ts)**を扱います。
参考リンク
- TypeScript Handbook — Modules(英語) — モジュール解決とimport/export構文の公式リファレンス
- MDN — JavaScript モジュール — ES Modulesの仕組みの日本語解説
TypeScriptクイズに挑戦するこの章で学んだTypeScriptの知識を、4択クイズでアウトプットして定着させよう
