- Day 27:Moduleで「ファイル分割」と「役割分担」を身につける
- Moduleとは何か:ファイルごとに「小さな世界」を作る
- exportとimportの基本
- 今回の構成をざっくり設計する
- Step 1:types.tsで「型」をまとめる
- Step 2:products.tsで「商品データ」と「商品関連の処理」をまとめる
- Step 3:utils.tsで「共通の便利関数」をまとめる
- Step 4:main.tsで「全体を組み立てて動かす」
- 4ファイル構成の全体像をもう一度整理する
- Day 27 練習テンプレート(まとめて確認用)
- Day 27のまとめ
- Day 27:Module演習 ― 小さな「多ファイル構成アプリ」を作る
- モジュール分割のゴールイメージをつかむ
- Step 1:types.ts ― 型定義をひとまとめにする
- Step 2:todo.ts ― ToDoの操作ロジックをまとめる
- Step 3:utils.ts ― 共通の便利関数をまとめる
- Step 4:main.ts ― 全体を組み立てて動かす
- Named ExportとDefault Exportの使い分けを整理する
- Day 27演習のテンプレートまとめ
- Day 27演習のまとめ
Day 27:Moduleで「ファイル分割」と「役割分担」を身につける
Day 27では、TypeScriptの Module(モジュール) を使って、 コードをきれいに「ファイルごとに分割する」練習をしていきます。
テーマはこうです。
商品管理プログラムを
types.ts/products.ts/utils.ts/main.tsに分割する。
ここで扱うキーワードは次のとおりです。
exportimport- Named Export
- Default Export
- ファイル分割
「ひとつの大きなファイルに全部書く」の卒業式のような回です。 役割ごとにファイルを分けることで、 コードがぐっと読みやすく、保守しやすくなっていきます。
Moduleとは何か:ファイルごとに「小さな世界」を作る
Moduleのざっくりしたイメージ
TypeScript(そして現代のJavaScript)では、 1ファイル = 1モジュール という考え方が基本になっています。
- あるファイルの中で
exportしたものだけが、 他のファイルからimportできます。 exportしていないものは、そのファイルの「内側だけのもの」です。
この仕組みによって、
- 「外に見せたいもの」
- 「内側だけで使いたいもの」
をきれいに分けることができます。
exportとimportの基本
Named Export(名前付きエクスポート)
Named Exportは、
「複数のものを名前付きで外に出す」方法
です。
// types.ts
export type Product = {
id: number;
name: string;
price: number;
};
export type Category = "book" | "food" | "toy";
TypeScriptこのように export を付けると、 他のファイルから次のように import できます。
// main.ts
import { Product, Category } from "./types";
TypeScript{ Product, Category }のように、 波括弧{}の中に名前を並べてインポートするのが特徴です。- 名前は基本的に「そのまま」使われます(別名を付けることもできます)。
Default Export(デフォルトエクスポート)
Default Exportは、
「そのモジュールの“代表”をひとつだけ外に出す」方法
です。
// utils.ts
export default function formatPrice(price: number): string {
return `${price.toLocaleString()}円`;
}
TypeScriptこのように export default を付けると、 他のファイルから次のように import できます。
// main.ts
import formatPrice from "./utils";
TypeScript- 波括弧
{}を使わずに、 好きな名前でインポートできるのが特徴です。 - そのモジュールの「メイン機能」をひとつだけ外に出したいときに向いています。
今回の構成をざっくり設計する
商品管理プログラムを、次の4ファイルに分けます。
types.ts- 型定義(Productなど)をまとめる
products.ts- 商品データや商品関連の関数をまとめる
utils.ts- 共通の便利関数(ユーティリティ)をまとめる
main.ts- 実際に処理を動かす「エントリーポイント」
この構成は、実務でもよく使われるパターンです。
Step 1:types.tsで「型」をまとめる
まずは、商品管理で使う型を types.ts に集めます。
// types.ts
// 商品管理で使う型定義をまとめるファイル
// 商品の型
export type Product = {
id: number; // 商品ID
name: string; // 商品名
price: number; // 価格
category: Category; // カテゴリ
};
// カテゴリの型
export type Category = "book" | "food" | "toy";
// 在庫情報の型
export type Stock = {
productId: number; // 商品ID
quantity: number; // 在庫数
};
TypeScriptポイント:
- すべて
exportを付けているので、 他のファイルからimport { Product, Category, Stock }として使えます。 - 型定義をひとつのファイルにまとめることで、 「型の一覧」を見たいときに
types.tsを開けばよくなります。
Step 2:products.tsで「商品データ」と「商品関連の処理」をまとめる
次に、商品データと商品を扱う関数を products.ts にまとめます。
// products.ts
// 商品データと商品関連の処理をまとめるファイル
import { Product, Category, Stock } from "./types";
// 商品一覧(サンプルデータ)
export const products: Product[] = [
{ id: 1, name: "TypeScript入門", price: 2000, category: "book" },
{ id: 2, name: "クッキー詰め合わせ", price: 1500, category: "food" },
{ id: 3, name: "ぬいぐるみ", price: 3000, category: "toy" },
];
// 在庫情報(サンプルデータ)
export const stocks: Stock[] = [
{ productId: 1, quantity: 10 },
{ productId: 2, quantity: 5 },
{ productId: 3, quantity: 2 },
];
// 商品IDから商品を検索する関数
export function findProductById(id: number): Product | undefined {
return products.find((p) => p.id === id);
}
// カテゴリで商品を絞り込む関数
export function filterProductsByCategory(category: Category): Product[] {
return products.filter((p) => p.category === category);
}
// 商品IDから在庫数を取得する関数
export function getStockQuantity(productId: number): number {
const stock = stocks.find((s) => s.productId === productId);
return stock ? stock.quantity : 0;
}
TypeScript重要ポイント:
import { Product, Category, Stock } from "./types";types.tsでexportした型を、ここでimportして使っています。
products/stocks/findProductByIdなどをexportしているので、main.tsから商品関連の機能をまとめて使えます。- 「商品に関することは
products.tsを見れば分かる」という 役割分担ができています。
Step 3:utils.tsで「共通の便利関数」をまとめる
次に、価格表示などの共通処理を utils.ts にまとめます。
ここでは、Default ExportとNamed Exportの両方を使ってみます。
// utils.ts
// 共通の便利関数をまとめるファイル
// 価格を「1,234円」のような文字列に整形する関数(Default Export)
export default function formatPrice(price: number): string {
return `${price.toLocaleString()}円`;
}
// 商品名と価格をまとめて表示用文字列にする関数(Named Export)
export function formatProductLine(name: string, price: number): string {
return `${name} - ${formatPrice(price)}`;
}
TypeScriptポイント:
export defaultを使ったformatPriceは、 「このモジュールの代表的な関数」という位置づけです。formatProductLineは Named Exportとして、 他の便利関数と並べて増やしていけるイメージです。
Step 4:main.tsで「全体を組み立てて動かす」
最後に、main.ts で各モジュールを組み合わせて、 商品管理プログラムを動かします。
// main.ts
// 商品管理プログラムのエントリーポイント
// 型や商品関連の処理をインポート
import { Product, Category } from "./types";
import {
products,
findProductById,
filterProductsByCategory,
getStockQuantity,
} from "./products";
// ユーティリティ関数をインポート
import formatPrice, { formatProductLine } from "./utils";
// 商品一覧を表示する関数
function showAllProducts(): void {
console.log("=== 商品一覧 ===");
products.forEach((product) => {
const line = formatProductLine(product.name, product.price);
const stock = getStockQuantity(product.id);
console.log(`${line} / 在庫: ${stock}個`);
});
}
// カテゴリ別に商品を表示する関数
function showProductsByCategory(category: Category): void {
console.log(`=== カテゴリ: ${category} の商品 ===`);
const filtered = filterProductsByCategory(category);
filtered.forEach((product) => {
console.log(
`${product.name} / 価格: ${formatPrice(product.price)} / 在庫: ${getStockQuantity(
product.id
)}個`
);
});
}
// 商品IDで詳細を表示する関数
function showProductDetail(id: number): void {
console.log(`=== 商品詳細 (ID: ${id}) ===`);
const product = findProductById(id);
if (!product) {
console.log("商品が見つかりませんでした");
return;
}
console.log(`商品名: ${product.name}`);
console.log(`価格: ${formatPrice(product.price)}`);
console.log(`カテゴリ: ${product.category}`);
console.log(`在庫: ${getStockQuantity(product.id)}個`);
}
// 実行例
showAllProducts();
console.log("");
showProductsByCategory("book");
console.log("");
showProductDetail(2);
TypeScriptここが大事なポイントです:
main.tsは「アプリ全体の流れ」を書く場所として、 ロジックの細かい部分はproducts.tsやutils.tsに任せています。- これにより、
main.tsを読むだけで 「このプログラムが何をしているか」がざっくり分かるようになります。 - 役割ごとにファイルを分けることで、 読みやすさ・変更のしやすさ・テストのしやすさが一気に上がります。
4ファイル構成の全体像をもう一度整理する
ここまでの構成を、もう一度ざっくりまとめます。
types.ts- 型定義(Product / Category / Stock)
- 他のファイルから
import { Product, Category, Stock }として使う
products.ts- 商品データ(
products) - 在庫データ(
stocks) - 商品検索・カテゴリ絞り込み・在庫取得などの関数
- 商品データ(
utils.ts- 価格表示などの共通ユーティリティ関数
formatPrice(Default Export)formatProductLine(Named Export)
main.ts- 全体の流れを組み立てるエントリーポイント
- 商品一覧表示・カテゴリ別表示・詳細表示などの処理
このようにモジュールを分けることで、
- 「型はここ」
- 「商品関連のロジックはここ」
- 「表示用の整形はここ」
- 「アプリの入口はここ」
というふうに、コードの「地図」がはっきりしてきます。
Day 27 練習テンプレート(まとめて確認用)
最後に、4ファイル分をまとめて確認できるように、 もう一度コードを並べておきます。
types.ts
// types.ts
export type Product = {
id: number;
name: string;
price: number;
category: Category;
};
export type Category = "book" | "food" | "toy";
export type Stock = {
productId: number;
quantity: number;
};
TypeScriptproducts.ts
// products.ts
import { Product, Category, Stock } from "./types";
export const products: Product[] = [
{ id: 1, name: "TypeScript入門", price: 2000, category: "book" },
{ id: 2, name: "クッキー詰め合わせ", price: 1500, category: "food" },
{ id: 3, name: "ぬいぐるみ", price: 3000, category: "toy" },
];
export const stocks: Stock[] = [
{ productId: 1, quantity: 10 },
{ productId: 2, quantity: 5 },
{ productId: 3, quantity: 2 },
];
export function findProductById(id: number): Product | undefined {
return products.find((p) => p.id === id);
}
export function filterProductsByCategory(category: Category): Product[] {
return products.filter((p) => p.category === category);
}
export function getStockQuantity(productId: number): number {
const stock = stocks.find((s) => s.productId === productId);
return stock ? stock.quantity : 0;
}
TypeScriptutils.ts
// utils.ts
export default function formatPrice(price: number): string {
return `${price.toLocaleString()}円`;
}
export function formatProductLine(name: string, price: number): string {
return `${name} - ${formatPrice(price)}`;
}
TypeScriptmain.ts
// main.ts
import { Category } from "./types";
import {
products,
findProductById,
filterProductsByCategory,
getStockQuantity,
} from "./products";
import formatPrice, { formatProductLine } from "./utils";
function showAllProducts(): void {
console.log("=== 商品一覧 ===");
products.forEach((product) => {
const line = formatProductLine(product.name, product.price);
const stock = getStockQuantity(product.id);
console.log(`${line} / 在庫: ${stock}個`);
});
}
function showProductsByCategory(category: Category): void {
console.log(`=== カテゴリ: ${category} の商品 ===`);
const filtered = filterProductsByCategory(category);
filtered.forEach((product) => {
console.log(
`${product.name} / 価格: ${formatPrice(product.price)} / 在庫: ${getStockQuantity(
product.id
)}個`
);
});
}
function showProductDetail(id: number): void {
console.log(`=== 商品詳細 (ID: ${id}) ===`);
const product = findProductById(id);
if (!product) {
console.log("商品が見つかりませんでした");
return;
}
console.log(`商品名: ${product.name}`);
console.log(`価格: ${formatPrice(product.price)}`);
console.log(`カテゴリ: ${product.category}`);
console.log(`在庫: ${getStockQuantity(product.id)}個`);
}
// 実行例
showAllProducts();
console.log("");
showProductsByCategory("book");
console.log("");
showProductDetail(2);
TypeScriptDay 27のまとめ
Day 27では、
- Moduleの基本(
export/import) - Named ExportとDefault Exportの違い
- 「型」「商品ロジック」「ユーティリティ」「エントリーポイント」を それぞれ別ファイルに分けるファイル分割の考え方
- 商品管理プログラムを
types.ts/products.ts/utils.ts/main.tsに分割して動かす流れ
を、ステップバイステップで見てきました。
モジュール分割は、 小さなスクリプトから「ちゃんとしたアプリ」に育てていくうえで、 欠かせない考え方です。
ぜひ、今日の構成をベースに、
cart.tsを追加してカート機能をモジュール化するuser.tsを追加してユーザー管理をモジュール化する
など、少しずつファイルを増やしながら、 「役割ごとにモジュールを分ける感覚」を育ててみてください。
Day 27:Module演習 ― 小さな「多ファイル構成アプリ」を作る
Day 27の演習テーマは、
3〜5ファイル構成のプログラムを作る。
というものです。 ここまで学んできた export / import / Named Export / Default Export を総動員して、 「ひとつのファイルに全部書く」スタイルから卒業していきます。
ここでは、初心者の方でもイメージしやすい ToDo管理ミニアプリ を題材にして、
types.ts(型定義)todo.ts(ToDo操作ロジック)utils.ts(ユーティリティ)main.ts(エントリーポイント)
という 4ファイル構成でステップバイステップに組み立てていきます。
モジュール分割のゴールイメージをつかむ
まずは、今回作る構成のざっくりしたイメージです。
types.ts- ToDoの型やステータスなど、「型」をまとめるファイル
todo.ts- ToDoの追加・完了・一覧取得など、「ロジック」をまとめるファイル
utils.ts- 日付フォーマットなど、「便利関数」をまとめるファイル
main.ts- 実際に処理を動かす「入口」のファイル
このように役割ごとに分けることで、
- 「型はここ」
- 「ToDoの操作はここ」
- 「表示用の整形はここ」
- 「アプリの流れはここ」
というふうに、コードの地図がはっきりしてきます。
Step 1:types.ts ― 型定義をひとまとめにする
まずは、ToDo管理で使う型を types.ts に集めます。
// types.ts
// ToDo管理で使う型定義をまとめるファイル
// ToDoのステータス
export type TodoStatus = "pending" | "done";
// ToDoアイテムの型
export type TodoItem = {
id: number; // 一意なID
title: string; // タイトル
description?: string; // 説明(任意)
status: TodoStatus; // ステータス
createdAt: Date; // 作成日時
};
TypeScriptここでのポイント:
- すべて
exportを付けているので、他のファイルからimport { TodoItem, TodoStatus }として使えます。 - 型定義をひとつのファイルにまとめることで、「型の一覧」を見たいときは
types.tsを開けばOK、という分かりやすい構造になります。
Step 2:todo.ts ― ToDoの操作ロジックをまとめる
次に、ToDoの追加・完了・一覧取得などのロジックを todo.ts にまとめます。
// todo.ts
// ToDoの操作ロジックをまとめるファイル
import { TodoItem, TodoStatus } from "./types";
// 内部で管理するToDo一覧(ここでは簡単のためメモリ上の配列)
const todos: TodoItem[] = [];
// 次のIDを生成するためのカウンタ
let nextId = 1;
// ToDoを追加する関数(Named Export)
export function addTodo(title: string, description?: string): TodoItem {
const newTodo: TodoItem = {
id: nextId++,
title,
description,
status: "pending",
createdAt: new Date(),
};
todos.push(newTodo);
return newTodo;
}
// IDを指定してToDoを完了にする関数
export function completeTodo(id: number): boolean {
const todo = todos.find((t) => t.id === id);
if (!todo) {
return false; // 見つからなかった
}
todo.status = "done";
return true;
}
// ステータスでToDoを絞り込む関数
export function getTodosByStatus(status: TodoStatus): TodoItem[] {
return todos.filter((t) => t.status === status);
}
// 全てのToDoを取得する関数
export function getAllTodos(): TodoItem[] {
// 外から直接配列をいじられないようにコピーを返す
return [...todos];
}
TypeScript重要なポイント:
todosとnextIdはexportしていないので、todo.tsの「内側だけの状態」として隠されています。- 外からは
addTodo/completeTodo/getTodosByStatus/getAllTodosを通じてしか操作できません。 これが「モジュールの中にロジックを閉じ込める」という考え方です。
Step 3:utils.ts ― 共通の便利関数をまとめる
次に、表示用の整形などのユーティリティを utils.ts にまとめます。 ここでは、Default ExportとNamed Exportの両方を使ってみます。
// utils.ts
// 共通の便利関数をまとめるファイル
// 日付を「YYYY-MM-DD HH:mm」の形式に整形する関数(Default Export)
export default function formatDate(date: Date): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hour = String(date.getHours()).padStart(2, "0");
const minute = String(date.getMinutes()).padStart(2, "0");
return `${year}-${month}-${day} ${hour}:${minute}`;
}
// ToDoアイテムを1行の文字列に整形する関数(Named Export)
import { TodoItem } from "./types";
export function formatTodoLine(todo: TodoItem): string {
const statusLabel = todo.status === "pending" ? "未完了" : "完了";
const created = formatDate(todo.createdAt);
return `[${statusLabel}] (${todo.id}) ${todo.title} / 作成: ${created}`;
}
TypeScriptポイント:
formatDateはexport defaultで出しているので、 他のファイルからはimport formatDate from "./utils";のように、 好きな名前でインポートできます。formatTodoLineは Named Exportとして、 他のユーティリティ関数と並べて増やしていけるイメージです。
Step 4:main.ts ― 全体を組み立てて動かす
最後に、main.ts で各モジュールを組み合わせて、 ToDo管理ミニアプリを動かします。
// main.ts
// ToDo管理ミニアプリのエントリーポイント
import { TodoStatus } from "./types";
import {
addTodo,
completeTodo,
getAllTodos,
getTodosByStatus,
} from "./todo";
import formatDate, { formatTodoLine } from "./utils";
// 全てのToDoを表示する関数
function showAllTodos(): void {
console.log("=== 全てのToDo ===");
const todos = getAllTodos();
if (todos.length === 0) {
console.log("ToDoはまだありません");
return;
}
todos.forEach((todo) => {
console.log(formatTodoLine(todo));
});
}
// ステータス別にToDoを表示する関数
function showTodosByStatus(status: TodoStatus): void {
console.log(`=== ステータス: ${status} のToDo ===`);
const todos = getTodosByStatus(status);
if (todos.length === 0) {
console.log("該当するToDoはありません");
return;
}
todos.forEach((todo) => {
console.log(formatTodoLine(todo));
});
}
// 実行例:いくつかToDoを追加して操作してみる
function main(): void {
console.log("=== ToDo管理ミニアプリ ===");
console.log("現在時刻:", formatDate(new Date()));
console.log("");
// ToDoを追加
const t1 = addTodo("TypeScriptの勉強をする", "Genericsを復習");
const t2 = addTodo("買い物に行く", "牛乳とパン");
const t3 = addTodo("運動する");
console.log("追加したToDoのID:", t1.id, t2.id, t3.id);
console.log("");
// 全てのToDoを表示
showAllTodos();
console.log("");
// 2番目のToDoを完了にする
const success = completeTodo(t2.id);
console.log(`ID ${t2.id} のToDo完了処理:`, success ? "成功" : "失敗");
console.log("");
// ステータス別に表示
showTodosByStatus("pending");
console.log("");
showTodosByStatus("done");
}
// エントリーポイントを呼び出す
main();
TypeScriptここでの大事なポイント:
main.tsは「アプリ全体の流れ」を書く場所として、 ロジックの細かい部分はtodo.tsやutils.tsに任せています。- これにより、
main.tsを読むだけで 「このプログラムが何をしているか」がざっくり分かるようになります。 importの行を見ると、「このファイルがどのモジュールに依存しているか」が一目で分かります。
Named ExportとDefault Exportの使い分けを整理する
ここまでの例で、両方のスタイルを使いました。 改めて、使い分けのイメージを整理しておきます。
- Named Export
- 複数の関数・型・定数を「名前付き」で外に出したいときに使います。
export function addTodo(...) {}import { addTodo } from "./todo";- 「このモジュールにはいろいろ入っているよ」という感じ。
- Default Export
- 「このモジュールの代表」をひとつだけ外に出したいときに使います。
export default function formatDate(...) {}import formatDate from "./utils";- 「このモジュールといえばこれ」というメイン機能に向いています。
どちらが正解というわけではなく、 「そのモジュールの役割」に合わせて選んでいくイメージです。
Day 27演習のテンプレートまとめ
最後に、4ファイル構成の全体をもう一度一覧できるようにまとめておきます。
types.ts
// types.ts
export type TodoStatus = "pending" | "done";
export type TodoItem = {
id: number;
title: string;
description?: string;
status: TodoStatus;
createdAt: Date;
};
TypeScripttodo.ts
// todo.ts
import { TodoItem, TodoStatus } from "./types";
const todos: TodoItem[] = [];
let nextId = 1;
export function addTodo(title: string, description?: string): TodoItem {
const newTodo: TodoItem = {
id: nextId++,
title,
description,
status: "pending",
createdAt: new Date(),
};
todos.push(newTodo);
return newTodo;
}
export function completeTodo(id: number): boolean {
const todo = todos.find((t) => t.id === id);
if (!todo) {
return false;
}
todo.status = "done";
return true;
}
export function getTodosByStatus(status: TodoStatus): TodoItem[] {
return todos.filter((t) => t.status === status);
}
export function getAllTodos(): TodoItem[] {
return [...todos];
}
TypeScriptutils.ts
// utils.ts
import { TodoItem } from "./types";
export default function formatDate(date: Date): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hour = String(date.getHours()).padStart(2, "0");
const minute = String(date.getMinutes()).padStart(2, "0");
return `${year}-${month}-${day} ${hour}:${minute}`;
}
export function formatTodoLine(todo: TodoItem): string {
const statusLabel = todo.status === "pending" ? "未完了" : "完了";
const created = formatDate(todo.createdAt);
return `[${statusLabel}] (${todo.id}) ${todo.title} / 作成: ${created}`;
}
TypeScriptmain.ts
// main.ts
import { TodoStatus } from "./types";
import {
addTodo,
completeTodo,
getAllTodos,
getTodosByStatus,
} from "./todo";
import formatDate, { formatTodoLine } from "./utils";
function showAllTodos(): void {
console.log("=== 全てのToDo ===");
const todos = getAllTodos();
if (todos.length === 0) {
console.log("ToDoはまだありません");
return;
}
todos.forEach((todo) => {
console.log(formatTodoLine(todo));
});
}
function showTodosByStatus(status: TodoStatus): void {
console.log(`=== ステータス: ${status} のToDo ===`);
const todos = getTodosByStatus(status);
if (todos.length === 0) {
console.log("該当するToDoはありません");
return;
}
todos.forEach((todo) => {
console.log(formatTodoLine(todo));
});
}
function main(): void {
console.log("=== ToDo管理ミニアプリ ===");
console.log("現在時刻:", formatDate(new Date()));
console.log("");
const t1 = addTodo("TypeScriptの勉強をする", "Genericsを復習");
const t2 = addTodo("買い物に行く", "牛乳とパン");
const t3 = addTodo("運動する");
console.log("追加したToDoのID:", t1.id, t2.id, t3.id);
console.log("");
showAllTodos();
console.log("");
const success = completeTodo(t2.id);
console.log(`ID ${t2.id} のToDo完了処理:`, success ? "成功" : "失敗");
console.log("");
showTodosByStatus("pending");
console.log("");
showTodosByStatus("done");
}
main();
TypeScriptDay 27演習のまとめ
今回の演習では、
export/importを使ってモジュール間で機能をやり取りする方法- Named ExportとDefault Exportの違いと使い分け
- 3〜5ファイル構成(ここでは4ファイル)のプログラムを 「型」「ロジック」「ユーティリティ」「エントリーポイント」に分けて設計する流れ
を、ToDo管理ミニアプリを通して体験しました。
小さなプログラムでも、 ファイルを分けて役割をはっきりさせるだけで、 ぐっと「本物のアプリ」っぽさが出てきます。
ぜひ、この構成をベースに、
user.tsを追加してユーザー管理をモジュール化するstorage.tsを追加してローカルストレージ保存機能をモジュール化する
など、少しずつファイルを増やしながら、 「モジュール分割のセンス」を育てていってください。
