typescript-conventions について
コーディングエージェントがプロジェクトの書式、命名、インポート、型安全性の規約に従うための TypeScript スキルです。Promise の処理、制御構文、エクスポート方法も定めています。
主な機能
- 01書式と差分最小化の規則
- 02ファイル命名とインポートの方針
- 03型安全性を重視し any を回避
- 04Promise の明示的な処理
- 05制御構文とエクスポートの統一
活用シーン
- 01TypeScript のコード変更を規約に沿って作成
- 02名前やインポートの一貫性を確認
- 03型定義とエラー処理を整備
- 04非同期コードとエクスポート方法を統一
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ exceptionless/ exceptionless --skill typescript-conventionsインストール後、Claude Code で/typescript-conventionsを入力して読み込まれたか確認します。
詳細
TypeScript の規約
スタイルとフォーマット
.editorconfigと ESLint + Prettier の設定を厳守してください- コミット前に
npm run formatを実行してください - 差分を最小限にする:必要な箇所のみ変更し、既存のフォーマットと構造を維持してください
- 周囲のコードスタイルに正確に合わせてください
プロジェクトのフロントエンド規則
- すべての制御フロー文(
if、for、whileなど)で、必ず波括弧を使用してください。 - 文を返すアロー関数では、必ずブロック本体を使用してください。プロジェクトコードでは、単一式の省略記法を避けてください。
- 識別子で略語を使用しないでください(
orgではなくorganization、filtではなくfilter)。 - 1 行に条件式と
returnを記述する形式は避け、読みやすい複数行のブロックを使用してください。
ファイルの命名
- ファイルとディレクトリには kebab-case を使用してください
- コンポーネントファイル:
user-profile.svelte - TypeScript ファイル:
api-client.ts、user-service.ts - テストファイル:
user-service.test.tsまたはuser-service.spec.ts
インポート
名前付きインポートを優先
// ✅ Good: Named imports
import { UserService, type User } from "$lib/services/user-service";
import { formatDate, formatNumber } from "$lib/utils/formatters";
// ❌ Avoid: Namespace imports (except allowed exceptions)
import * as utils from "$lib/utils";
- ほとんどのモジュールでは名前付きインポートを優先してください。名前空間インポートは、承認済みのパターン(shadcn の複合インポートなど)に限定してください。
許可される名前空間インポート
// ✅ Allowed: shadcn-svelte components
import * as Dialog from "$comp/ui/dialog";
import * as DropdownMenu from "$comp/ui/dropdown-menu";
// ✅ Allowed: Barrel exports
import * as Field from "$comp/ui/field";
型安全性
any を避ける
// ❌ Bad
function processData(data: any) { ... }
// ✅ Good: Use interfaces/types
interface UserData {
id: string;
name: string;
email: string;
}
function processData(data: UserData) { ... }
// ✅ Good: Use unknown for truly unknown data
function parseResponse(data: unknown): UserData {
if (isUserData(data)) {
return data;
}
throw new Error('Invalid data format');
}
型ガード
function isUserData(data: unknown): data is UserData {
return (
typeof data === "object" &&
data !== null &&
"id" in data &&
"name" in data &&
"email" in data
);
}
// Discriminated unions
type ApiResponse =
| { status: "success"; data: UserData }
| { status: "error"; error: string };
function handleResponse(response: ApiResponse) {
if (response.status === "success") {
// TypeScript knows response.data exists
return response.data;
}
// TypeScript knows response.error exists
throw new Error(response.error);
}
Promise の処理
必ず await する
// ✅ Good: Always await
const user = await fetchUser(id);
const [users, projects] = await Promise.all([fetchUsers(), fetchProjects()]);
// ❌ Bad: Fire and forget without handling
fetchUser(id); // Exception is lost!
エラー処理
// ✅ Good: try/catch with proper typing
async function loadUser(id: string): Promise<User | null> {
try {
const response = await api.get<User>(`/users/${id}`);
return response.data;
} catch (error) {
if (error instanceof ApiError) {
console.error("API Error:", error.message);
}
return null;
}
}
制御文
1 行の制御文には、すべて波括弧が必要です。
// ✅ Good: Always use braces
if (condition) {
doSomething();
}
for (const item of items) {
process(item);
}
// ❌ Bad: No braces
if (condition) doSomething();
インターフェースの命名
API 関連の型には、HTTP 動詞の接頭辞を使用してください。
// Request/Response interfaces
interface PostOrganizationRequest {
name: string;
billing_email: string;
}
interface GetOrganizationParams {
id: string;
}
interface PatchUserRequest {
name?: string;
email?: string;
}
エクスポートのパターン
// Named exports preferred
export function createUser(data: CreateUserRequest): Promise<User> { ... }
export type { User, CreateUserRequest };
// Re-export from barrel files
// src/lib/features/users/index.ts
export { createUser, updateUser, deleteUser } from './api.svelte';
export type { User, CreateUserRequest } from './models';
モダンな ES6 以降の機能
// Template literals
const message = `Hello, ${user.name}!`;
// Destructuring
const { id, name, email } = user;
const [first, ...rest] = items;
// Nullish coalescing
const displayName = user.nickname ?? user.name ?? "Anonymous";
// Optional chaining
const city = user?.address?.city;
// Object shorthand
const data = { id, name, createdAt: new Date() };
コメント
Markdown 対応