mcpField
T
typescript

typescript

github.com/martinholovsky开发48212 次安装48 周前更新
代码仓库

关于 typescript

面向 JARVIS 开发者的 TypeScript 技能,涵盖严格类型检查、运行时验证、测试、性能与安全实践。

主要功能

  1. 01严格编译与类型安全指导
  2. 02使用 Zod 验证外部数据
  3. 03品牌类型与类型守卫
  4. 04TDD 与验证流程
  5. 05性能和安全模式

适用场景

  1. 01为应用数据定义类型接口
  2. 02在运行时验证外部输入
  3. 03建模状态机和标识符
  4. 04测试和审查 TypeScript 修改

安装

复制下面的命令,在终端中运行。

$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 组件采用泛型模式
  • 严格检查空值,防止崩溃

2. 核心职责

2.1 基本原则
  1. 优先采用 TDD:先编写测试再实现——红、绿、重构
  2. 关注性能:采用记忆化、延迟加载和高效模式
  3. 始终启用严格模式:启用所有严格编译器选项,不走捷径
  4. 在边界处明确指定类型:始终为模块边界处的函数参数和返回值指定类型
  5. 运行时验证:TypeScript 类型在运行时会消失——对外部数据使用 Zod
  6. 不使用 any 逃生舱:使用 unknown 代替 any,然后通过类型守卫缩小类型范围
  7. 默认不可变:优先使用 readonly 和 as const 确保数据完整性
  8. 可辨识联合类型:对状态机和错误处理使用带标签的联合类型
  9. 品牌类型:为 ID 和敏感值创建名义类型

3. 技术栈与版本

3.1 推荐版本
包版本安全说明
typescript^5.3.0最新稳定版,改进了类型推断
zod^3.22.0运行时验证,以模式为先
@types/node^20.0.0与 Node.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. 严格配置:启用所有严格选项,以获得最高安全性
  2. 运行时验证:类型在运行时会消失——请使用 Zod
  3. 品牌类型:防止混淆 ID 和敏感值
  4. 类型守卫:通过谓词安全地缩小 unknown 数据的类型范围

请记住:TypeScript 仅在编译时进行检查。每个涉及外部数据的边界都必须进行运行时验证。


参考资料:

  • references/advanced-patterns.md - 复杂类型模式
  • references/security-examples.md - 安全的类型实践

评论

支持 Markdown