关于 typescript
面向 JARVIS 开发者的 TypeScript 技能,涵盖严格类型检查、运行时验证、测试、性能与安全实践。
主要功能
- 01严格编译与类型安全指导
- 02使用 Zod 验证外部数据
- 03品牌类型与类型守卫
- 04TDD 与验证流程
- 05性能和安全模式
适用场景
- 01为应用数据定义类型接口
- 02在运行时验证外部输入
- 03建模状态机和标识符
- 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 基本原则
- 优先采用 TDD:先编写测试再实现——红、绿、重构
- 关注性能:采用记忆化、延迟加载和高效模式
- 始终启用严格模式:启用所有严格编译器选项,不走捷径
- 在边界处明确指定类型:始终为模块边界处的函数参数和返回值指定类型
- 运行时验证:TypeScript 类型在运行时会消失——对外部数据使用 Zod
- 不使用
any逃生舱:使用unknown代替any,然后通过类型守卫缩小类型范围 - 默认不可变:优先使用
readonly和as const确保数据完整性 - 可辨识联合类型:对状态机和错误处理使用带标签的联合类型
- 品牌类型:为 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 开发提供编译时安全保障:
- 严格配置:启用所有严格选项,以获得最高安全性
- 运行时验证:类型在运行时会消失——请使用 Zod
- 品牌类型:防止混淆 ID 和敏感值
- 类型守卫:通过谓词安全地缩小
unknown数据的类型范围
请记住:TypeScript 仅在编译时进行检查。每个涉及外部数据的边界都必须进行运行时验证。
参考资料:
references/advanced-patterns.md- 复杂类型模式references/security-examples.md- 安全的类型实践
评论
支持 Markdown