mcpField
T
typescript

typescript

github.com/martinholovsky開発48212 回インストール48 週間前に更新
リポジトリ

typescript について

JARVIS の開発者向け TypeScript スキルです。厳格な型付け、ランタイム検証、テスト、性能、セキュリティの実践方法を扱います。

主な機能

  1. 01厳格なコンパイラー設定と型安全性
  2. 02Zod による外部データ検証
  3. 03ブランド型と型ガード
  4. 04TDD と検証のワークフロー
  5. 05性能とセキュリティのパターン

活用シーン

  1. 01アプリケーションデータの型付きインターフェース定義
  2. 02外部入力のランタイム検証
  3. 03ステートマシンと ID のモデル化
  4. 04TypeScript の変更のテストとレビュー

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/martinholovsky/claude-skills-generator --skill typescript

インストール後、Claude Code で/typescriptを入力して読み込まれたか確認します。

同梱ファイルreferences/advanced-patterns.mdreferences/security-examples.md

詳細

TypeScript 開発スキル

ファイル構成:このスキルでは分割構成を使用しています。高度なパターンやセキュリティの例については、references/ を参照してください。

1. 概要

このスキルでは、JARVIS AI アシスタント向けの TypeScript の専門知識を提供し、Vue コンポーネント、API ルート、3D レンダリング、状態管理など、コードベース全体で型安全性を確保します。

リスクレベル:中 — 型システムによって実行時エラーを防止し、契約を適用できますが、設定を誤るとセキュリティ上の欠陥につながる可能性があります

主なユースケース:

  • JARVIS システムのデータに対する型安全なインターフェースの定義
  • Zod スキーマを使用した実行時検証
  • 再利用可能な HUD コンポーネント向けのジェネリックパターン
  • クラッシュを防ぐ厳密な null チェック

2. 中核となる責務

2.1 基本原則
  1. まず TDD:実装の前にテストを記述します — レッド、グリーン、リファクタリング
  2. パフォーマンスを意識する:メモ化、遅延読み込み、効率的なパターンを適用します
  3. 常に厳格モード:すべての厳格なコンパイラーオプションを有効にします — 近道はしません
  4. 境界では型を明示する:モジュール境界では、常に関数のパラメーターと戻り値に型を指定します
  5. 実行時検証:TypeScript の型は実行時には消えるため、外部データには Zod を使用します
  6. any で逃げない:any ではなく unknown を使用し、型ガードで型を絞り込みます
  7. デフォルトで不変にする:データの整合性を保つため、readonly と as const を優先します
  8. 判別可能なユニオン:状態機械やエラー処理にはタグ付きユニオンを使用します
  9. ブランド型:ID や機密性の高い値には名目的な型を作成します

3. 技術スタックとバージョン

3.1 推奨バージョン
パッケージバージョンセキュリティに関する注意事項
typescript^5.3.0型推論が改善された最新の安定版
zod^3.22.0実行時検証、スキーマファースト
@types/node^20.0.0Node.js のバージョンに合わせます
3.2 コンパイラー設定
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "exactOptionalPropertyTypes": true,
    "noPropertyAccessFromIndexSignature": true,
    "forceConsistentCasingInFileNames": true,
    "verbatimModuleSyntax": true,
    "moduleResolution": "bundler",
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"]
  }
}

4. 実装パターン

4.1 セキュリティのためのブランド型
// types/branded.ts
declare const __brand: unique symbol

type Brand<T, B> = T & { [__brand]: B }

// ✅ Prevent accidental mixing of IDs
export type UserId = Brand<string, 'UserId'>
export type SessionId = Brand<string, 'SessionId'>
export type CommandId = Brand<string, 'CommandId'>

// Factory functions with validation
export function createUserId(id: string): UserId {
  if (!/^usr_[a-zA-Z0-9]{16}$/.test(id)) {
    throw new Error('Invalid user ID format')
  }
  return id as UserId
}

// ✅ Type system prevents mixing IDs
function getUser(id: UserId): User { /* ... */ }
function getSession(id: SessionId): Session { /* ... */ }

// This won't compile:
// getUser(sessionId) // Error: SessionId not assignable to UserId
4.2 状態管理のための判別可能なユニオン
// types/jarvis-state.ts

// ✅ Type-safe state machine
type JARVISState =
  | { status: 'idle' }
  | { status: 'listening'; startTime: number }
  | { status: 'processing'; commandId: CommandId }
  | { status: 'responding'; response: string; confidence: number }
  | { status: 'error'; error: JARVISError; retryCount: number }

// ✅ Exhaustive handling
function handleState(state: JARVISState): string {
  switch (state.status) {
    case 'idle':
      return 'Ready'
    case 'listening':
      return `Listening for ${Date.now() - state.startTime}ms`
    case 'processing':
      return `Processing ${state.commandId}`
    case 'responding':
      return `${state.response} (${state.confidence}%)`
    case 'error':
      return `Error: ${state.error.message}`
    default:
      // ✅ Compile-time exhaustiveness check
      const _exhaustive: never = state
      return _exhaustive
  }
}
4.3 Zod による実行時検証
// schemas/command.ts
import { z } from 'zod'

// ✅ Schema-first approach
export const commandSchema = z.object({
  id: z.string().regex(/^cmd_[a-zA-Z0-9]{16}$/),
  action: z.enum(['navigate', 'control', 'query', 'configure']),
  target: z.string().min(1).max(100),
  parameters: z.record(z.unknown()).optional(),
  timestamp: z.number().int().positive(),
  priority: z.number().min(0).max(10).default(5)
})

// ✅ Infer TypeScript type from schema
export type Command = z.infer<typeof commandSchema>

// ✅ Parse with full validation
export function parseCommand(data: unknown): Command {
  return commandSchema.parse(data)
}

// ✅ Safe parse for error handling
export function tryParseCommand(data: unknown): Command | null {
  const result = commandSchema.safeParse(data)
  return result.success ? result.data : null
}
4.4 HUD コンポーネント向けのジェネリックパターン
// ✅ Generic with constraints - ensures type safety for metrics
interface MetricConfig<T extends Record<string, number>> {
  metrics: T
  thresholds: { [K in keyof T]: { warning: number; critical: number } }
}

type SystemMetrics = { cpu: number; memory: number }
const config: MetricConfig<SystemMetrics> = {
  metrics: { cpu: 45, memory: 72 },
  thresholds: {
    cpu: { warning: 70, critical: 90 },
    memory: { warning: 80, critical: 95 }
  }
}
4.5 型を絞り込むための型ガード
// ✅ Type predicate for safe narrowing
function isSuccessResponse<T>(
  response: APIResponse<T>
): response is APIResponse<T> & { success: true; data: T } {
  return response.success && response.data !== undefined
}

// Usage - type automatically narrowed
if (isSuccessResponse(response)) {
  return response.data // ✅ Type is T, not T | undefined
}
4.6 JARVIS 向けのユーティリティ型
// types/utilities.ts

// ✅ Deep readonly for immutable state
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}

// ✅ Make specific keys required
type RequireKeys<T, K extends keyof T> = T & Required<Pick<T, K>>

// ✅ Extract event payloads
type EventPayload<T> = T extends { payload: infer P } ? P : never

// ✅ Async function return type
type AsyncReturnType<T extends (...args: any[]) => Promise<any>> =
  T extends (...args: any[]) => Promise<infer R> ? R : never

5. 実装ワークフロー(TDD)

ステップ 1:まず失敗するテストを書く
// tests/utils/command-parser.test.ts
import { describe, it, expect } from 'vitest'
import { parseCommand } from '@/utils/command-parser'

describe('parseCommand', () => {
  it('should parse valid command', () => {
    expect(parseCommand('open settings')).toEqual({
      action: 'open', target: 'settings', parameters: {}
    })
  })

  it('should extract parameters', () => {
    expect(parseCommand('set volume to 80')).toEqual({
      action: 'set', target: 'volume', parameters: { value: 80 }
    })
  })

  it('should throw on empty command', () => {
    expect(() => parseCommand('')).toThrow('Command cannot be empty')
  })
})
ステップ 2:テストを通すための最小限の実装を行う

テストをグリーンにするために必要なコードだけを記述します。

ステップ 3:必要に応じてリファクタリングする

テストをグリーンに保ちながら、コード品質を向上させます。

ステップ 4:完全な検証を実行する
npx vitest run              # Unit tests
npx eslint . --ext .ts,.tsx # Linting
npx tsc --noEmit            # Type checking

6. パフォーマンスパターン

6.1 メモ化
// ❌ BAD - Recalculates on every render
const processed = data.map(item => heavyTransform(item))

// ✅ GOOD - Memoized computation
import { computed } from 'vue'
const processed = computed(() => data.value.map(item => heavyTransform(item)))
6.2 遅延読み込み
// ❌ BAD - Loads everything upfront
import { HeavyChart } from '@/components/HeavyChart'

// ✅ GOOD - Lazy load heavy components
import { defineAsyncComponent } from 'vue'
const HeavyChart = defineAsyncComponent(() => import('@/components/HeavyChart'))
6.3 デバウンス/スロットリング
// ❌ BAD - API call on every keystroke
const handleSearch = (q: string) => fetchResults(q)

// ✅ GOOD - Debounced search (300ms delay)
import { useDebounceFn } from '@vueuse/core'
const debouncedSearch = useDebounceFn((q: string) => fetchResults(q), 300)
6.4 効率的なデータ構造
// ❌ BAD - O(n) lookup
const user = users.find(u => u.id === id)

// ✅ GOOD - O(1) lookup with Map
const userMap = new Map(users.map(u => [u.id, u]))
const user = userMap.get(id)

// ✅ GOOD - O(1) membership check with Set
const allowed = new Set(['read', 'write'])
const hasAccess = allowed.has(permission)
6.5 非同期処理の並列実行
// ❌ BAD - Sequential (total = sum of times)
const user = await fetchUser()
const metrics = await fetchMetrics()

// ✅ GOOD - Parallel (total = max of times)
const [user, metrics] = await Promise.all([fetchUser(), fetchMetrics()])

// ✅ GOOD - With error handling
const results = await Promise.allSettled([fetchUser(), fetchMetrics()])

7. セキュリティ基準

7.1 既知の脆弱性

TypeScript 自体には優れたセキュリティ実績があります。主なリスクは次のとおりです。

リスク領域説明軽減策
型消去型は実行時には存在しません外部データの検証には Zod を使用します
Any 型型チェックを無効にしますnoImplicitAny を有効にし、unknown を使用します
型アサーション型システムを回避できますas を避け、代わりに型ガードを使用します
7.2 OWASP Top 10 への対応
OWASP カテゴリーTypeScript による軽減策
A03 インジェクション型付き API によって、クエリーでの文字列補間を防ぎます
A04 安全でない設計強い型付けによって、安全なインターフェースを適用します
A08 ソフトウェアの完全性コンパイル時チェックによって、デプロイ前にエラーを検出します
7.3 安全な型パターン
// ❌ DANGEROUS - Type assertion bypasses safety
const userData = apiResponse as User

// ✅ SECURE - Runtime validation
const userData = userSchema.parse(apiResponse)

// ❌ DANGEROUS - any disables all checks
function process(data: any) { /* ... */ }

// ✅ SECURE - unknown requires narrowing
function process(data: unknown) {
  const validated = commandSchema.parse(data)
  // Now safely typed
}

8. テストと品質

// Type testing with vitest
import { expectTypeOf } from 'vitest'

describe('Type Safety', () => {
  it('should enforce branded types', () => {
    expectTypeOf(createUserId('usr_1234567890123456')).toEqualTypeOf<UserId>()
  })
})

// Schema validation tests
describe('Command Schema', () => {
  it('should reject invalid IDs', () => {
    expect(() => commandSchema.parse({ id: 'invalid' })).toThrow()
  })

  it('should accept valid commands', () => {
    const result = commandSchema.parse({ id: 'cmd_1234567890123456', action: 'query' })
    expect(result.action).toBe('query')
  })
})

9. よくある誤りとアンチパターン

9.1 重大なセキュリティ上のアンチパターン
禁止:外部データに型アサーションを使用する
// ❌ DANGEROUS - No runtime validation
const user = JSON.parse(data) as User

// ✅ SECURE - Validate at runtime
const user = userSchema.parse(JSON.parse(data))
禁止:Null/Undefined のチェックを無視する
// ❌ DANGEROUS - Runtime crash if undefined
function getConfig(key: string) {
  return config[key].value  // May be undefined!
}

// ✅ SECURE - Explicit null handling
function getConfig(key: string): string | undefined {
  return config[key]?.value
}
禁止:チェックせずにインデックスシグネチャを使用する
// ❌ DANGEROUS - No type safety
const handlers: Record<string, Handler> = {}
handlers['cmd'].execute()  // May be undefined!

// ✅ SECURE - Explicit lookup with check
const handler = handlers['cmd']
if (handler) {
  handler.execute()
}
9.2 パフォーマンス上のアンチパターン
避ける:型を過度に複雑にする
// ❌ BAD - Unreadable, slow compilation
type DeepPartialRecord<T> = T extends object
  ? { [K in keyof T]?: DeepPartialRecord<T[K]> }
  : T extends Array<infer U>
  ? Array<DeepPartialRecord<U>>
  : T

// ✅ GOOD - Simple, clear types
interface PartialConfig {
  theme?: Partial<ThemeConfig>
  metrics?: Partial<MetricsConfig>
}

10. デプロイ前チェックリスト

フェーズ 1:コードを書く前
  • 先に失敗するテストを書く(TDD)
  • 新機能の型とインターフェースを定義する
  • 外部データ用の Zod スキーマを計画する
  • パフォーマンスが重要な処理経路を特定する
  • コードベースの既存のパターンを確認する
フェーズ 2:実装中
  • tsconfig で strict: true を有効にする
  • noUncheckedIndexedAccess: true を有効にする
  • noImplicitAny: true を有効にする
  • 本番コードで any 型を使用しない
  • すべての外部データを Zod で検証する
  • 機密性の高い ID にはブランド型を使う
  • 状態機械には判別可能なユニオンを使う
  • コストの高い計算にメモ化を適用する
  • 重いコンポーネントには遅延読み込みを使う
  • 検索には効率的なデータ構造(Map/Set)を使う
フェーズ 3:コミット前
  • すべてのテストが成功する(npx vitest run)
  • lint エラーがない(npx eslint .)
  • TypeScript エラーがない(npx tsc --noEmit)
  • すべての API 入力を実行時に検証する
  • 外部データに対する型アサーションを使用しない
  • 必要な箇所にパフォーマンスパターンを適用する

11. まとめ

TypeScript は、JARVIS の開発にコンパイル時の安全性をもたらします。

  1. 厳格な設定:最大限の安全性を確保するため、すべての strict オプションを有効にします
  2. 実行時の検証:型は実行時には消えるため、Zod を使います
  3. ブランド型:ID や機密性の高い値の混同を防ぎます
  4. 型ガード:述語を使って unknown データを安全に絞り込みます

覚えておいてください:TypeScript がチェックするのはコンパイル時のみです。外部データとのすべての境界で、実行時の検証が必要です。


参考資料:

  • references/advanced-patterns.md - 複雑な型のパターン
  • references/security-examples.md - 安全な型付けの実践例

コメント