メインコンテンツにスキップ

📘 TypeScript基礎

📖 定義

TypeScriptはMicrosoftが開発したJavaScriptのスーパーセット言語です。JavaScriptに静的型システムを追加することで、コード作成時点でエラーを発見し、開発ツールの自動補完機能を向上させます。TypeScriptコードはコンパイルを通じてJavaScriptに変換されて実行されます。

🎯 比喩で理解する

契約書の比喩

TypeScriptを契約書に例えると:

JavaScript (口頭約束)
A: "明日の午後に会おう!"
B: "うん、わかった!"

翌日...
A: "なんで来ないの?"
B: "午後2時だと思ってたけど?"
A: "いや、午後5時って言ったよ!"

問題:
- 具体的な時間が明示されていない
- 誤解が発生
- 実行時点で問題発見

---

TypeScript (正式な契約書)
契約書:
- 日付: 2024年1月15日
- 時間: 午後5時ちょうど
- 場所: 江南駅2番出口
- 参加者: A、B (型を明示!)

利点:
- 明確な約束
- 誤解不可能
- 契約書作成時点で検証
- 法的効力

工場の比喩

JavaScript工場 (検品なし)
部品入荷:
- どんな部品でもOK
- サイズ確認なし
- 規格チェックなし

組立ライン:
- 組立中に問題発見
- "このネジが合わないね?"
- "この部品のサイズがおかしいけど?"

結果:
- 不良品発生
- 時間/コスト浪費
- 顧客不満

---

TypeScript工場 (厳格な検品)
部品入荷:
✓ サイズ測定
✓ 規格確認
✓ 品質検査
✗ 不良品返品

組立ライン:
- 部品が正確に合致
- 問題なく組立
- 迅速な生産

結果:
- 高品質製品
- 効率的生産
- 顧客満足

レゴブロックの比喩

JavaScript レゴ
- どんなブロックでも挿してみることができる
- 合うかどうかは挿してみないとわからない
- 大きな作品を作る時に混乱

TypeScript レゴ
- ブロックごとにラベル
- "このブロックは2x4ブロック"
- "この穴には2x4しか入らない"
- 合わないものは最初から挿せない

利点:
- 説明書を見て正確に組立
- ミス防止
- 大きな作品も問題なし

⚙️ 動作原理

1. 静的型システム

// JavaScript (動的型)
let name = "金哲秀";
name = 123; // OK! 問題なし
name = true; // OK! 問題なし
name = null; // OK! 問題なし

// 実行中に問題発生
function greet(person) {
return "Hello, " + person.name; // personが何かわからない
}

greet({ name: "哲秀" }); // OK
greet("哲秀"); // 💥 Runtime Error!
greet(null); // 💥 Runtime Error!

// TypeScript (静的型)
let name: string = "金哲秀";
name = 123; // ❌ Compile Error! (型エラー)
name = true; // ❌ Compile Error!
name = null; // ❌ Compile Error!

// 関数パラメータと戻り値の型を明示
function greet(person: { name: string }): string {
return "Hello, " + person.name;
}

greet({ name: "哲秀" }); // ✅ OK
greet("哲秀"); // ❌ Compile Error! (型エラー)
greet(null); // ❌ Compile Error!

// 利点:
// 1. コード作成時点でエラー発見
// 2. IDE自動補完
// 3. リファクタリングが安全
// 4. ドキュメント化の役割

2. コンパイル過程

// TypeScript コード
// app.ts
interface User {
id: number;
name: string;
email: string;
}

function getUser(id: number): User {
return {
id: id,
name: "金哲秀",
email: "kim@example.com"
};
}

const user: User = getUser(1);
console.log(user.name);

// ↓ TypeScript コンパイラ (tsc)

// JavaScript コード
// app.js
function getUser(id) {
return {
id: id,
name: "金哲秀",
email: "kim@example.com"
};
}

var user = getUser(1);
console.log(user.name);

// 特徴:
// 1. 型情報削除
// 2. ES5、ES6、ESNextなどに変換可能
// 3. ブラウザ/Node.jsで実行
// 4. ソースマップ生成 (デバッグ用)

3. 型推論

// TypeScriptは型を自動的に推論

// 明示的な型
let name: string = "金哲秀";
let age: number = 25;

// 型推論 (自動)
let name = "金哲秀"; // stringと推論
let age = 25; // numberと推論
let isActive = true; // booleanと推論

// 配列
let numbers = [1, 2, 3]; // number[]と推論
let names = ["金", "李"]; // string[]と推論

// オブジェクト
let user = {
name: "金哲秀",
age: 25
};
// { name: string; age: number }と推論

// 関数の戻り値の型
function add(a: number, b: number) {
return a + b; // number返却と推論
}

// 利点:
// - 型の記述を減らす
// - それでも型安全性
// - 可読性向上

💡 実際の例

基本型

// 1. プリミティブ型
let name: string = "金哲秀";
let age: number = 25;
let isStudent: boolean = true;
let nothing: null = null;
let notDefined: undefined = undefined;

// 2. 配列
let numbers: number[] = [1, 2, 3, 4, 5];
let names: Array<string> = ["金哲秀", "李英姫"];

// 混合配列
let mixed: (number | string)[] = [1, "two", 3, "four"];

// 3. タプル (固定長、各位置ごとに型)
let person: [string, number] = ["金哲秀", 25];
let coordinate: [number, number] = [10, 20];

// ❌ 誤った使用
person = [25, "金哲秀"]; // 順序が違う
person = ["金哲秀"]; // 長さが違う
person = ["金哲秀", 25, true]; // 長さが違う

// 4. enum (列挙型)
enum Color {
Red, // 0
Green, // 1
Blue // 2
}

let color: Color = Color.Red;
console.log(color); // 0

// 値の指定
enum Status {
Pending = "PENDING",
Approved = "APPROVED",
Rejected = "REJECTED"
}

let status: Status = Status.Pending;
console.log(status); // "PENDING"

// 5. any (すべての型を許可)
let anything: any = "文字列";
anything = 123; // OK
anything = true; // OK
anything = null; // OK

// ⚠️ anyはTypeScriptの利点を失う!
// 必要な場合のみ使用

// 6. unknown (anyより安全)
let value: unknown = "文字列";

// ❌ すぐに使用不可
// console.log(value.length);

// ✅ 型チェック後に使用
if (typeof value === "string") {
console.log(value.length); // OK
}

// 7. void (戻り値なし)
function logMessage(message: string): void {
console.log(message);
// return なし
}

// 8. never (絶対に戻らない)
function throwError(message: string): never {
throw new Error(message);
// 関数が絶対に終わらない
}

function infiniteLoop(): never {
while (true) {
// 無限ループ
}
}

// 9. object
let obj: object = { name: "金哲秀" };
let arr: object = [1, 2, 3];
let func: object = function() {};

インターフェース

// Interface: オブジェクトの構造定義

// 1. 基本インターフェース
interface User {
id: number;
name: string;
email: string;
}

const user: User = {
id: 1,
name: "金哲秀",
email: "kim@example.com"
};

// ❌ プロパティ漏れ
const invalidUser: User = {
id: 1,
name: "金哲秀"
// email がない!
};

// 2. オプショナルプロパティ
interface Product {
id: number;
name: string;
price: number;
description?: string; // オプショナル (あってもなくてもよい)
}

const product1: Product = {
id: 1,
name: "ノートパソコン",
price: 1000000,
description: "高性能ノートパソコン"
};

const product2: Product = {
id: 2,
name: "マウス",
price: 30000
// description なくてもOK
};

// 3. 読み取り専用プロパティ
interface Config {
readonly apiUrl: string;
readonly timeout: number;
}

const config: Config = {
apiUrl: "https://api.example.com",
timeout: 5000
};

// ❌ 変更不可
config.apiUrl = "https://api2.example.com";

// 4. 関数型
interface SearchFunc {
(query: string, page: number): string[];
}

const search: SearchFunc = (query, page) => {
// 実装
return ["結果1", "結果2"];
};

// 5. インデックスシグネチャ (動的プロパティ)
interface StringMap {
[key: string]: string;
}

const translations: StringMap = {
hello: "こんにちは",
goodbye: "さようなら",
thanks: "ありがとうございます"
// キー・値ともにstring
};

// 6. インターフェースの拡張 (継承)
interface Person {
name: string;
age: number;
}

interface Student extends Person {
studentId: string;
grade: number;
}

const student: Student = {
name: "金哲秀",
age: 20,
studentId: "2024001",
grade: 2
};

// 7. 複数インターフェースの拡張
interface Printable {
print(): void;
}

interface Scannable {
scan(): void;
}

interface Printer extends Printable, Scannable {
model: string;
}

const printer: Printer = {
model: "HP-1234",
print() {
console.log("印刷中...");
},
scan() {
console.log("スキャン中...");
}
};

Type Alias

// Type Alias: 型に名前をつける

// 1. 基本型エイリアス
type UserID = number;
type UserName = string;

let id: UserID = 123;
let name: UserName = "金哲秀";

// 2. ユニオン型
type Status = "pending" | "approved" | "rejected";

let orderStatus: Status = "pending";
orderStatus = "approved"; // OK
// orderStatus = "shipped"; // ❌ Error

// 3. オブジェクト型
type Point = {
x: number;
y: number;
};

const point: Point = { x: 10, y: 20 };

// 4. 関数型
type GreetFunction = (name: string) => string;

const greet: GreetFunction = (name) => {
return `Hello, ${name}!`;
};

// 5. インターセクション型 (交差型)
type Person = {
name: string;
age: number;
};

type Employee = {
employeeId: string;
department: string;
};

type EmployeePerson = Person & Employee;

const employee: EmployeePerson = {
name: "金哲秀",
age: 30,
employeeId: "E123",
department: "開発チーム"
// すべてのプロパティ必要!
};

// 6. Interface vs Type
// Interface (拡張可能)
interface Animal {
name: string;
}

interface Animal {
age: number; // マージされる!
}

const animal: Animal = {
name: "犬",
age: 3
};

// Type (拡張不可)
type Car = {
model: string;
};

// ❌ Error: 重複宣言不可
// type Car = {
// year: number;
// };

// いつ何を使う?
// - Interface: オブジェクト構造、クラス、拡張可能
// - Type: ユニオン、インターセクション、ユーティリティ型

ジェネリック (Generics)

// ジェネリック: 再利用可能な型

// ❌ ジェネリックなし (重複コード)
function numberIdentity(value: number): number {
return value;
}

function stringIdentity(value: string): string {
return value;
}

function booleanIdentity(value: boolean): boolean {
return value;
}

// ✅ ジェネリック使用 (一つに統合)
function identity<T>(value: T): T {
return value;
}

const num = identity<number>(123); // number
const str = identity<string>("hello"); // string
const bool = identity<boolean>(true); // boolean

// 型推論でより簡単に
const num2 = identity(123); // numberと推論
const str2 = identity("hello"); // stringと推論

// 2. ジェネリック配列
function getFirstElement<T>(arr: T[]): T | undefined {
return arr[0];
}

const firstNum = getFirstElement([1, 2, 3]); // number
const firstName = getFirstElement(["a", "b", "c"]); // string

// 3. ジェネリックインターフェース
interface Box<T> {
value: T;
}

const numberBox: Box<number> = { value: 123 };
const stringBox: Box<string> = { value: "hello" };

// 4. ジェネリッククラス
class Stack<T> {
private items: T[] = [];

push(item: T): void {
this.items.push(item);
}

pop(): T | undefined {
return this.items.pop();
}

peek(): T | undefined {
return this.items[this.items.length - 1];
}
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
numberStack.push(3);
console.log(numberStack.pop()); // 3

const stringStack = new Stack<string>();
stringStack.push("a");
stringStack.push("b");

// 5. ジェネリック制約 (Constraints)
interface HasLength {
length: number;
}

function logLength<T extends HasLength>(item: T): void {
console.log(item.length);
}

logLength("hello"); // OK (string has length)
logLength([1, 2, 3]); // OK (array has length)
// logLength(123); // ❌ Error (number doesn't have length)

// 6. 複数のジェネリック型
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}

const p1 = pair<string, number>("age", 25);
const p2 = pair("name", "金哲秀"); // 型推論

// 7. ジェネリックのデフォルト値
interface Container<T = string> {
value: T;
}

const container1: Container = { value: "hello" }; // string
const container2: Container<number> = { value: 123 };

ユーティリティ型

// TypeScript組み込みユーティリティ型

// 1. Partial<T> (すべてのプロパティをオプショナルに)
interface User {
id: number;
name: string;
email: string;
}

function updateUser(id: number, updates: Partial<User>): void {
// updatesは一部のプロパティのみでOK
}

updateUser(1, { name: "金哲秀" }); // OK
updateUser(2, { email: "new@example.com" }); // OK
updateUser(3, { name: "李英姫", email: "lee@example.com" }); // OK

// 2. Required<T> (すべてのプロパティを必須に)
interface PartialUser {
id?: number;
name?: string;
email?: string;
}

type RequiredUser = Required<PartialUser>;

const user: RequiredUser = {
id: 1,
name: "金哲秀",
email: "kim@example.com"
// すべてのプロパティが必須!
};

// 3. Readonly<T> (すべてのプロパティを読み取り専用に)
interface Config {
apiUrl: string;
timeout: number;
}

const config: Readonly<Config> = {
apiUrl: "https://api.example.com",
timeout: 5000
};

// ❌ 変更不可
// config.apiUrl = "https://api2.example.com";

// 4. Pick<T, K> (特定のプロパティのみ選択)
interface Product {
id: number;
name: string;
price: number;
description: string;
stock: number;
}

type ProductPreview = Pick<Product, "id" | "name" | "price">;

const preview: ProductPreview = {
id: 1,
name: "ノートパソコン",
price: 1000000
// description、stock不要
};

// 5. Omit<T, K> (特定のプロパティを除外)
type ProductWithoutStock = Omit<Product, "stock">;

const product: ProductWithoutStock = {
id: 1,
name: "ノートパソコン",
price: 1000000,
description: "高性能ノートパソコン"
// stock 除外
};

// 6. Record<K, T> (キー・値のマッピング)
type PageInfo = Record<string, { title: string; url: string }>;

const pages: PageInfo = {
home: { title: "ホーム", url: "/" },
about: { title: "紹介", url: "/about" },
contact: { title: "お問い合わせ", url: "/contact" }
};

// 7. Exclude<T, U> (ユニオンから型を除外)
type AllStatus = "pending" | "approved" | "rejected" | "draft";
type ActiveStatus = Exclude<AllStatus, "draft">;
// "pending" | "approved" | "rejected"

// 8. Extract<T, U> (ユニオンから型を抽出)
type Status = "pending" | "approved" | "rejected";
type PositiveStatus = Extract<Status, "approved">;
// "approved"

// 9. NonNullable<T> (null、undefinedを削除)
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>;
// string

// 10. ReturnType<T> (関数の戻り値の型を抽出)
function getUser() {
return {
id: 1,
name: "金哲秀",
email: "kim@example.com"
};
}

type User = ReturnType<typeof getUser>;
// { id: number; name: string; email: string }

JavaScript → TypeScript 変換

// JavaScript コード
// user.js
function createUser(name, age, email) {
return {
name: name,
age: age,
email: email,
greet: function() {
return "Hello, " + this.name + "!";
}
};
}

function getUsers() {
return fetch('/api/users')
.then(res => res.json())
.then(data => data.users);
}

const user = createUser("金哲秀", 25, "kim@example.com");
console.log(user.greet());

// ↓ TypeScriptに変換

// user.ts
interface User {
name: string;
age: number;
email: string;
greet(): string;
}

function createUser(name: string, age: number, email: string): User {
return {
name,
age,
email,
greet() {
return `Hello, ${this.name}!`;
}
};
}

interface ApiResponse {
users: User[];
}

async function getUsers(): Promise<User[]> {
const response = await fetch('/api/users');
const data: ApiResponse = await response.json();
return data.users;
}

const user: User = createUser("金哲秀", 25, "kim@example.com");
console.log(user.greet());

// 利点:
// 1. 型安全性
// 2. IDE自動補完
// 3. リファクタリングが簡単
// 4. バグの事前防止

ReactでTypeScript

// React コンポーネント (JavaScript)
// Button.jsx
function Button({ label, onClick, disabled }) {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
}

// ↓ TypeScriptに変換

// Button.tsx
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}

function Button({ label, onClick, disabled = false }: ButtonProps) {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
}

// またはReact.FCを使用
const Button: React.FC<ButtonProps> = ({ label, onClick, disabled = false }) => {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
};

// 使用
<Button
label="クリック"
onClick={() => console.log('clicked')}
disabled={false}
/>

// ❌ 誤った使用
<Button
label={123} // ❌ Error: labelはstring
onClick="handleClick" // ❌ Error: onClickは関数
/>

// 2. StateとHooks
import { useState, useEffect } from 'react';

interface User {
id: number;
name: string;
email: string;
}

function UserProfile() {
// Stateの型指定
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
setLoading(true);
fetch('/api/user')
.then(res => res.json())
.then((data: User) => {
setUser(data);
setLoading(false);
})
.catch((err: Error) => {
setError(err.message);
setLoading(false);
});
}, []);

if (loading) return <div>ロード中...</div>;
if (error) return <div>エラー: {error}</div>;
if (!user) return <div>ユーザーなし</div>;

return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}

// 3. Event Handler
interface FormProps {
onSubmit: (data: { name: string; email: string }) => void;
}

function Form({ onSubmit }: FormProps) {
const [name, setName] = useState<string>('');
const [email, setEmail] = useState<string>('');

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit({ name, email });
};

const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
};

return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={handleNameChange}
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button type="submit">送信</button>
</form>
);
}

よくあるエラーと解決

// 1. Object is possibly 'null' or 'undefined'
// ❌ Error
function getLength(text: string | null) {
return text.length; // ❌ textがnullの可能性
}

// ✅ 解決1: nullチェック
function getLength(text: string | null) {
if (text === null) {
return 0;
}
return text.length; // ✅ OK
}

// ✅ 解決2: Optional Chaining
function getLength(text: string | null) {
return text?.length ?? 0;
}

// 2. Type 'X' is not assignable to type 'Y'
// ❌ Error
interface User {
name: string;
age: number;
}

const user: User = {
name: "金哲秀"
// age 漏れ!
};

// ✅ 解決: すべてのプロパティを追加
const user: User = {
name: "金哲秀",
age: 25
};

// 3. Cannot find name 'X'
// ❌ Error
console.log(process.env.API_URL); // ❌ processの型がない

// ✅ 解決: @typesをインストール
// npm install --save-dev @types/node

// 4. Property 'X' does not exist on type 'Y'
// ❌ Error
const obj = {};
obj.name = "金哲秀"; // ❌ nameプロパティがない

// ✅ 解決1: 型定義
interface Obj {
name: string;
}

const obj: Obj = { name: "" };
obj.name = "金哲秀"; // ✅ OK

// ✅ 解決2: インデックスシグネチャ
const obj: { [key: string]: string } = {};
obj.name = "金哲秀"; // ✅ OK

// 5. Type assertions (型アサーション)
const input = document.getElementById('myInput');
// inputはHTMLElement | null

// ❌ Error
input.value = "hello"; // HTMLElementにvalueがない

// ✅ 解決: 型アサーション
const input = document.getElementById('myInput') as HTMLInputElement;
input.value = "hello"; // ✅ OK

// または
const input = <HTMLInputElement>document.getElementById('myInput');
input.value = "hello"; // ✅ OK

// 6. Non-null assertion operator (!)
const element = document.getElementById('app');
// elementはHTMLElement | null

// ❌ Error
element.innerHTML = "hello"; // nullの可能性

// ✅ 解決: nullチェック
if (element) {
element.innerHTML = "hello";
}

// または ! 演算子 (確実な場合のみ!)
element!.innerHTML = "hello"; // nullではないとアサート

// 7. Type narrowing (型の絞り込み)
function printValue(value: string | number) {
// ❌ Error
// console.log(value.toUpperCase()); // numberにはtoUpperCaseがない

// ✅ 解決: typeofで型チェック
if (typeof value === "string") {
console.log(value.toUpperCase()); // ✅ OK (string)
} else {
console.log(value.toFixed(2)); // ✅ OK (number)
}
}

🤔 よくある質問

Q1. TypeScriptをなぜ使うべきですか?

A: いくつかの理由があります:

// 1. バグの事前防止
// JavaScript
function calculateTotal(items) {
let total = 0;
items.forEach(item => {
total += item.price;
});
return total;
}

// 実行中に発見されるバグ:
calculateTotal(null); // 💥 Runtime Error!
calculateTotal([{ name: "商品" }]); // 💥 priceがundefined

// TypeScript
interface Item {
name: string;
price: number;
}

function calculateTotal(items: Item[]): number {
let total = 0;
items.forEach(item => {
total += item.price;
});
return total;
}

// コード作成中に発見:
calculateTotal(null); // ❌ Compile Error!
calculateTotal([{ name: "商品" }]); // ❌ Compile Error! (priceがない)

// 2. 自動補完とIntelliSense
const user = {
name: "金哲秀",
age: 25,
email: "kim@example.com"
};

// JavaScript: user. 入力時に自動補完なし
// TypeScript: user. 入力時にname、age、emailが自動補完!

// 3. 安全なリファクタリング
// 関数シグネチャ変更時
function greet(name) {
return `Hello, ${name}!`;
}

// TypeScriptはすべての呼び出し箇所を見つけてエラー表示
// JavaScriptは実行してみないと問題発見できない

// 4. ドキュメント化の役割
// JavaScript
function createUser(name, age, email, isAdmin) {
// nameがstringかnumberかわからない
// ageが必須かオプショナルかわからない
// isAdminが何かわからない
}

// TypeScript
interface CreateUserParams {
name: string;
age: number;
email: string;
isAdmin?: boolean; // オプショナル、デフォルト値false
}

function createUser(params: CreateUserParams): User {
// すべてが明確!
}

// 5. チーム協業
// TypeScriptコードを見れば:
// - 関数がどんな引数を受け取るか
// - どんな型を返すか
// - どんなプロパティがあるか
// すべて明確!

// 結論:
// JavaScript: 素早いプロトタイプ、簡単なスクリプト
// TypeScript: 大規模プロジェクト、チーム協業、メンテナンス

Q2. TypeScriptの欠点はありませんか?

A: 欠点もあります:

// 欠点1: 学習曲線
// JavaScriptだけ知っていればすぐ開始可能
// TypeScriptは型システムの学習が必要

// 欠点2: 初期設定
// JavaScript: ファイルを作ってすぐ実行
// TypeScript: 設定ファイル、コンパイルが必要

// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
// ... 多くのオプション
}
}

// 欠点3: 型の記述時間
// JavaScript
const users = [
{ name: "金哲秀", age: 25 },
{ name: "李英姫", age: 30 }
];

// TypeScript (型定義が必要)
interface User {
name: string;
age: number;
}

const users: User[] = [
{ name: "金哲秀", age: 25 },
{ name: "李英姫", age: 30 }
];

// 欠点4: サードパーティライブラリ
// 型定義がないライブラリはanyを使う必要がある
import someLib from 'old-library'; // 型がない
// @types/old-library のインストールが必要

// 欠点5: コンパイル時間
// 大きなプロジェクトはコンパイル時間がかかる
// (ただしesbuild、SWCで大幅改善)

// しかし利点が欠点よりはるかに大きい!
// 特に大規模プロジェクトでは必須!

Q3. anyを使ってもいいですか?

A: できるだけ避けるのが良いです:

// ❌ anyの乱用 (TypeScriptの利点を失う)
function processData(data: any) {
return data.map((item: any) => {
return {
id: item.id,
value: item.value
};
});
}

// ✅ 適切な型を使用
interface DataItem {
id: number;
value: string;
}

function processData(data: DataItem[]) {
return data.map(item => {
return {
id: item.id,
value: item.value
};
});
}

// anyを使ってもいい場合:
// 1. サードパーティライブラリ (型定義がない)
import oldLib from 'very-old-library';
const result: any = oldLib.doSomething();

// 2. JSONパース (構造がわからない)
const data: any = JSON.parse(jsonString);
// その後型ガードで検証

// 3. レガシーコードのマイグレーション
// JavaScript → TypeScript 段階的移行時

// 代替案:
// - unknown: anyより安全
// - ジェネリック: 型を保存
// - 型ガード: ランタイム検証

Q4. interface vs type、いつ何を使いますか?

A: 状況によって異なります:

// Interface使用 (推奨)
// 1. オブジェクト構造定義
interface User {
id: number;
name: string;
}

// 2. 拡張可能
interface Student extends User {
studentId: string;
}

// 3. 宣言のマージ (Declaration Merging)
interface Window {
myCustomProperty: string;
}

interface Window {
anotherProperty: number;
}
// 二つの宣言がマージされる!

// Type使用
// 1. ユニオン型
type Status = "pending" | "approved" | "rejected";

// 2. インターセクション型
type Employee = Person & Worker;

// 3. 関数型
type GreetFunction = (name: string) => string;

// 4. ユーティリティ型
type PartialUser = Partial<User>;
type PickedUser = Pick<User, "id" | "name">;

// 5. タプル
type Point = [number, number];

// 6. Mapped Types
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};

// 推奨:
// - 基本的にinterfaceを使用
// - ユニオン/インターセクションが必要ならtypeを使用
// - 一貫性を維持 (プロジェクト内)

Q5. TypeScriptプロジェクトをどのように始めますか?

A: いくつかの方法があります:

# 1. Create React App (TypeScriptテンプレート)
npx create-react-app my-app --template typescript

# 2. Next.js (TypeScript)
npx create-next-app@latest my-app --typescript

# 3. Vite (TypeScript)
npm create vite@latest my-app -- --template react-ts

# 4. 既存プロジェクトにTypeScriptを追加
npm install --save-dev typescript @types/react @types/react-dom

# tsconfig.json 生成
npx tsc --init

# 5. 純粋なTypeScriptプロジェクト
mkdir my-project
cd my-project
npm init -y
npm install --save-dev typescript @types/node

# tsconfig.json 生成
npx tsc --init

# src/index.ts ファイル生成
echo 'console.log("Hello TypeScript!");' > src/index.ts

# コンパイル
npx tsc

# 実行
node dist/index.js
// tsconfig.json (推奨設定)
{
"compilerOptions": {
// ターゲットJavaScriptバージョン
"target": "ES2020",

// モジュールシステム
"module": "ESNext",
"moduleResolution": "node",

// JSXサポート (React)
"jsx": "react-jsx",

// 厳格な型チェック
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,

// モジュール解析
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,

// 出力ディレクトリ
"outDir": "./dist",

// ソースマップ生成
"sourceMap": true,

// 不要なチェックをスキップ
"skipLibCheck": true,

// パスエイリアス
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

🎓 次のステップ

TypeScriptの基礎を理解したら、次を学習してみましょう:

  1. Reactとは? (ドキュメント作成予定) - TypeScriptでReact開発
  2. バンドラーとは? - TypeScriptビルド環境
  3. TDDとは? (ドキュメント作成予定) - TypeScriptでテスト作成

🎬 まとめ

TypeScriptはJavaScriptをより安全で生産的にします:

  • 型安全性: バグの事前防止
  • 自動補完: 開発速度向上
  • リファクタリング: 安全なコード変更
  • ドキュメント化: コードがドキュメント

大規模プロジェクトやチーム協業でTypeScriptは必須です。学習曲線はありますが、投資する価値は十分にあります!