mastering-typescript について
mastering-typescriptは、TypeScriptアプリの開発や移行に取り組む開発者向けのスキルです。最新の型システムパターン、フレームワークの例、Zodによる検証、ツールチェーン設定を、参考資料や設定ファイルとともにまとめています。
主な機能
- 01最新のTypeScript型システムパターン
- 02ジェネリクス、マップ型、条件型
- 03ReactとNestJSの例
- 04Zodスキーマ検証
- 05ツールチェーン設定ガイド
- 06移行ガイドと設定ファイル
活用シーン
- 01型安全なReactまたはNestJSアプリの構築
- 02JavaScriptコードベースの段階的な移行
- 03Zodを使った検証付きAPI契約の設計
- 04TypeScriptプロジェクトのツール設定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ spillwavesolutions/ mastering-typescript-skill --skill mastering-typescriptインストール後、Claude Code で/mastering-typescriptを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/eslint-template.jstsconfig-template.jsonreferences/enterprise-patterns.mdgenerics.mdnestjs-integration.mdreact-integration.mdtoolchain.mdtype-system.mdscripts/validate-setup.sh詳細
モダン TypeScript のマスター
TypeScript 5.9+ でエンタープライズグレードの型安全なアプリケーションを構築しましょう。
互換性: TypeScript 5.9+、Node.js 22 LTS、Vite 7、NestJS 11、React 19
クイックスタート
# Initialize TypeScript project with ESM
pnpm create vite@latest my-app --template vanilla-ts
cd my-app && pnpm install
# Configure strict TypeScript
cat > tsconfig.json << 'EOF'
{
"compilerOptions": {
"target": "ES2024",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
EOF
このスキルを使うべき場面
次のような場合に使用します:
- 型安全な React、NestJS、Node.js アプリケーションを構築する場合
- JavaScript のコードベースを TypeScript に移行する場合
- 高度な型パターン(ジェネリクス、マップ型、条件型)を実装する場合
- モダンな TypeScript ツールチェーン(Vite、pnpm、ESLint)を設定する場合
- Zod バリデーションを用いた型安全な API コントラクトを設計する場合
- TypeScript のアプローチを Java や Python と比較する場合
プロジェクトセットアップチェックリスト
TypeScript プロジェクトを始める前に:
- [ ] Use pnpm for package management (faster, disk-efficient)
- [ ] Configure ESM-first (type: "module" in package.json)
- [ ] Enable strict mode in tsconfig.json
- [ ] Set up ESLint with @typescript-eslint
- [ ] Add Prettier for consistent formatting
- [ ] Configure Vitest for testing
型システム早見表
プリミティブ型
const name: string = "Alice";
const age: number = 30;
const active: boolean = true;
const id: bigint = 9007199254740991n;
const key: symbol = Symbol("unique");
ユニオン型とインターセクション型
// Union: value can be one of several types
type Status = "pending" | "approved" | "rejected";
// Intersection: value must satisfy all types
type Employee = Person & { employeeId: string };
// Discriminated union for type-safe handling
type Result<T> =
| { success: true; data: T }
| { success: false; error: string };
function handleResult<T>(result: Result<T>): T | null {
if (result.success) {
return result.data; // TypeScript knows data exists here
}
console.error(result.error);
return null;
}
型ガード
// typeof guard
function process(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase();
}
return value.toFixed(2);
}
// Custom type guard
interface User { type: "user"; name: string }
interface Admin { type: "admin"; permissions: string[] }
function isAdmin(person: User | Admin): person is Admin {
return person.type === "admin";
}
satisfies 演算子(TS 5.0+)
推論を保持しつつ型の適合性を検証します:
// Problem: Type assertion loses specific type info
const colors1 = {
red: "#ff0000",
green: "#00ff00"
} as Record<string, string>;
colors1.red.toUpperCase(); // OK, but red could be undefined
// Solution: satisfies preserves literal types
const colors2 = {
red: "#ff0000",
green: "#00ff00"
} satisfies Record<string, string>;
colors2.red.toUpperCase(); // OK, and TypeScript knows red exists
ジェネリクスのパターン
基本的なジェネリック関数
function first<T>(items: T[]): T | undefined {
return items[0];
}
const num = first([1, 2, 3]); // number | undefined
const str = first(["a", "b"]); // string | undefined
制約付きジェネリクス
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): T {
console.log(item.length);
return item;
}
logLength("hello"); // OK: string has length
logLength([1, 2, 3]); // OK: array has length
logLength(42); // Error: number has no length
ジェネリックな API レスポンスラッパー
interface ApiResponse<T> {
data: T;
status: number;
timestamp: Date;
}
async function fetchUser(id: string): Promise<ApiResponse<User>> {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
return {
data,
status: response.status,
timestamp: new Date()
};
}
ユーティリティ型リファレンス
| 型 | 目的 | 例 |
|---|---|---|
Partial<T> | すべてのプロパティを省略可能にする | Partial<User> |
Required<T> | すべてのプロパティを必須にする | Required<Config> |
Pick<T, K> | 特定のプロパティを選択する | Pick<User, "id" | "name"> |
Omit<T, K> | 特定のプロパティを除外する | Omit<User, "password"> |
Record<K, V> | キーと値の型を持つオブジェクト | Record<string, number> |
ReturnType<F> | 関数の戻り値型を抽出する | ReturnType<typeof fn> |
Parameters<F> | 関数のパラメーターを抽出する | Parameters<typeof fn> |
Awaited<T> | Promise 型をアンラップする | Awaited<Promise<User>> |
条件型
// Basic conditional type
type IsString<T> = T extends string ? true : false;
// Extract array element type
type ArrayElement<T> = T extends (infer E)[] ? E : never;
type Numbers = ArrayElement<number[]>; // number
type Strings = ArrayElement<string[]>; // string
// Practical: Extract Promise result type
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;
マップ型
// Make all properties readonly
type Immutable<T> = {
readonly [K in keyof T]: T[K];
};
// Make all properties nullable
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
// Create getter functions for each property
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Person { name: string; age: number }
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number }
フレームワークとの統合
TypeScript での React
// Typed functional component
interface ButtonProps {
label: string;
onClick: () => void;
variant?: "primary" | "secondary";
}
const Button: React.FC<ButtonProps> = ({ label, onClick, variant = "primary" }) => (
<button className={variant} onClick={onClick}>
{label}
</button>
);
// Typed hooks
const [count, setCount] = useState<number>(0);
const userRef = useRef<HTMLInputElement>(null);
TypeScript での NestJS
// Type-safe DTO with class-validator
import { IsString, IsEmail, MinLength } from 'class-validator';
class CreateUserDto {
@IsString()
@MinLength(2)
name: string;
@IsEmail()
email: string;
}
// Or with Zod (modern approach)
import { z } from 'zod';
const CreateUserSchema = z.object({
name: z.string().min(2),
email: z.string().email()
});
type CreateUserDto = z.infer<typeof CreateUserSchema>;
詳細なパターンについては react-integration.md と nestjs-integration.md を参照してください。
Zod によるバリデーション
import { z } from 'zod';
// Define schema
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(100),
email: z.string().email(),
role: z.enum(["user", "admin", "moderator"]),
createdAt: z.coerce.date()
});
// Infer TypeScript type from schema
type User = z.infer<typeof UserSchema>;
// Validate at runtime
function parseUser(data: unknown): User {
return UserSchema.parse(data); // Throws ZodError if invalid
}
// Safe parsing (returns result object)
const result = UserSchema.safeParse(data);
if (result.success) {
console.log(result.data); // Typed as User
} else {
console.error(result.error.issues);
}
モダンなツールチェーン(2025)
| ツール | バージョン | 目的 |
|---|---|---|
| TypeScript | 5.9+ | 型チェックとコンパイル |
| Node.js | 22 LTS | ランタイム環境 |
| Vite | 7.x | ビルドツールおよび開発サーバー |
| pnpm | 9.x | パッケージマネージャー |
| ESLint | 9.x | flat config によるリンティング |
| Vitest | 3.x | テストフレームワーク |
| Prettier | 3.x | コードフォーマット |
ESLint の Flat Config(ESLint 9+)
// eslint.config.js
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.strictTypeChecked,
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
}
);
移行戦略
段階的な移行
- tsconfig.json に
allowJs: trueとcheckJs: falseを追加する - ファイルを一度に 1 つずつ
.jsから.tsにリネームする - 型注釈を徐々に追加する
- より厳格なオプションを段階的に有効にする
段階的な型付けのための JSDoc
// Before full migration, use JSDoc
/**
* @param {string} name
* @param {number} age
* @returns {User}
*/
function createUser(name, age) {
return { name, age };
}
包括的な移行ガイドについては enterprise-patterns.md を参照してください。
よくある間違い
| 間違い | 問題 | 修正方法 |
|---|---|---|
any を多用する | 型安全性が損なわれる | unknown を使用して絞り込む |
| strict モードを無視する | null/undefined のバグを見逃す | すべての strict オプションを有効にする |
型アサーション(as) | 型エラーを隠してしまう可能性がある | satisfies または型ガードを使用する |
| 単純なユニオンに enum を使う | ランタイムコードが生成される | 代わりにリテラルユニオンを使用する |
| API データを検証しない | ランタイムの型の不一致 | 境界で Zod を使用する |
言語間の比較
| 機能 | TypeScript | Java | Python |
|---|---|---|---|
| 型システム | 構造的 | 名前的 | 段階的(ダックタイピング) |
| Null 許容性 | 明示的(T | null) | @Nullable アノテーション | typing による Optional |
| ジェネリクス | 型レベル、消去される | 型レベル、消去される | typing によるランタイム |
| インターフェース | 構造的マッチング | 実装が必須 | プロトコル(3.8+) |
| Enum | 避ける(ユニオンを使用) | 第一級 | Enum クラス |
参照ファイル
- type-system.md — 型システムの完全ガイド
- generics.md — 高度なジェネリクスのパターン
- enterprise-patterns.md — エラー処理、バリデーション、アーキテクチャ
- react-integration.md — React + TypeScript のパターン
- nestjs-integration.md — NestJS による API 開発
- toolchain.md — モダンなビルドツール
アセット
- tsconfig-template.json — 厳格なエンタープライズ向け設定
- eslint-template.js — ESLint 9 のフラット設定
スクリプト
- validate-setup.sh — TypeScript 環境を検証
コメント
Markdown 対応