name: typescript description: JARVIS AI助手的类型安全开发模式 model: sonnet risk_level: 中等 version: 1.0.0
TypeScript开发技能
文件组织: 此技能采用拆分结构。请参阅
references/以获取高级模式和安全性示例。
1. 概述
此技能为JARVIS AI助手提供TypeScript专业知识,确保整个代码库的类型安全,包括Vue组件、API路由、3D渲染和状态管理。
风险级别: 中等 - 类型系统防止运行时错误,强制合约,但配置不当可能导致安全漏洞
主要用例:
- 定义JARVIS系统数据的类型安全接口
- 使用Zod模式进行运行时验证
- 可重用HUD组件的通用模式
- 严格空检查以防止崩溃
2. 核心职责
2.1 基本原则
- 测试驱动开发优先: 先写测试再实现 - 红、绿、重构
- 性能意识: 应用记忆化、懒加载和高效模式
- 始终启用严格模式: 启用所有严格编译器选项 - 无捷径
- 在边界处显式类型: 在模块边界处始终类型化函数参数和返回值
- 运行时验证: TypeScript类型在运行时消失 - 使用Zod处理外部数据
- 无任何转义舱口: 使用
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 }
// ✅ 防止意外混合ID
export type UserId = Brand<string, 'UserId'>
export type SessionId = Brand<string, 'SessionId'>
export type CommandId = Brand<string, 'CommandId'>
// 带验证的工厂函数
export function createUserId(id: string): UserId {
if (!/^usr_[a-zA-Z0-9]{16}$/.test(id)) {
throw new Error('无效的用户ID格式')
}
return id as UserId
}
// ✅ 类型系统防止混合ID
function getUser(id: UserId): User { /* ... */ }
function getSession(id: SessionId): Session { /* ... */ }
// 这不会编译:
// getUser(sessionId) // 错误: SessionId不可分配给UserId
4.2 判别联合类型用于状态
// types/jarvis-state.ts
// ✅ 类型安全状态机
type JARVISState =
| { status: 'idle' }
| { status: 'listening'; startTime: number }
| { status: 'processing'; commandId: CommandId }
| { status: 'responding'; response: string; confidence: number }
| { status: 'error'; error: JARVISError; retryCount: number }
// ✅ 穷举处理
function handleState(state: JARVISState): string {
switch (state.status) {
case 'idle':
return '就绪'
case 'listening':
return `监听中 ${Date.now() - state.startTime}毫秒`
case 'processing':
return `处理中 ${state.commandId}`
case 'responding':
return `${state.response} (${state.confidence}%)`
case 'error':
return `错误: ${state.error.message}`
default:
// ✅ 编译时穷举检查
const _exhaustive: never = state
return _exhaustive
}
}
4.3 使用Zod进行运行时验证
// schemas/command.ts
import { z } from 'zod'
// ✅ 模式优先方法
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)
})
// ✅ 从模式推断TypeScript类型
export type Command = z.infer<typeof commandSchema>
// ✅ 完整验证解析
export function parseCommand(data: unknown): Command {
return commandSchema.parse(data)
}
// ✅ 安全解析用于错误处理
export function tryParseCommand(data: unknown): Command | null {
const result = commandSchema.safeParse(data)
return result.success ? result.data : null
}
4.4 HUD组件的通用模式
// ✅ 带约束的泛型 - 确保指标类型安全
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 类型守卫用于缩小范围
// ✅ 类型谓词用于安全缩小范围
function isSuccessResponse<T>(
response: APIResponse<T>
): response is APIResponse<T> & { success: true; data: T } {
return response.success && response.data !== undefined
}
// 用法 - 类型自动缩小
if (isSuccessResponse(response)) {
return response.data // ✅ 类型为T,非T | undefined
}
4.6 JARVIS实用类型
// types/utilities.ts
// ✅ 深度只读用于不可变状态
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}
// ✅ 使特定键为必需
type RequireKeys<T, K extends keyof T> = T & Required<Pick<T, K>>
// ✅ 提取事件载荷
type EventPayload<T> = T extends { payload: infer P } ? P : never
// ✅ 异步函数返回类型
type AsyncReturnType<T extends (...args: any[]) => Promise<any>> =
T extends (...args: any[]) => Promise<infer R> ? R : never
5. 实现工作流程(测试驱动开发)
步骤1: 先写失败测试
// tests/utils/command-parser.test.ts
import { describe, it, expect } from 'vitest'
import { parseCommand } from '@/utils/command-parser'
describe('parseCommand', () => {
it('应解析有效命令', () => {
expect(parseCommand('open settings')).toEqual({
action: 'open', target: 'settings', parameters: {}
})
})
it('应提取参数', () => {
expect(parseCommand('set volume to 80')).toEqual({
action: 'set', target: 'volume', parameters: { value: 80 }
})
})
it('空命令应抛出异常', () => {
expect(() => parseCommand('')).toThrow('命令不能为空')
})
})
步骤2: 实现最小化代码以通过测试
只写使测试通过的代码。
步骤3: 必要时重构
在保持测试通过的同时改进代码质量。
步骤4: 运行完整验证
npx vitest run # 单元测试
npx eslint . --ext .ts,.tsx # 代码检查
npx tsc --noEmit # 类型检查
6. 性能模式
6.1 记忆化
// ❌ 坏 - 每次渲染都重新计算
const processed = data.map(item => heavyTransform(item))
// ✅ 好 - 记忆化计算
import { computed } from 'vue'
const processed = computed(() => data.value.map(item => heavyTransform(item)))
6.2 懒加载
// ❌ 坏 - 提前加载所有内容
import { HeavyChart } from '@/components/HeavyChart'
// ✅ 好 - 懒加载重型组件
import { defineAsyncComponent } from 'vue'
const HeavyChart = defineAsyncComponent(() => import('@/components/HeavyChart'))
6.3 防抖/节流
// ❌ 坏 - 每次按键都调用API
const handleSearch = (q: string) => fetchResults(q)
// ✅ 好 - 防抖搜索(300毫秒延迟)
import { useDebounceFn } from '@vueuse/core'
const debouncedSearch = useDebounceFn((q: string) => fetchResults(q), 300)
6.4 高效数据结构
// ❌ 坏 - O(n)查找
const user = users.find(u => u.id === id)
// ✅ 好 - 使用Map进行O(1)查找
const userMap = new Map(users.map(u => [u.id, u]))
const user = userMap.get(id)
// ✅ 好 - 使用Set进行O(1)成员检查
const allowed = new Set(['read', 'write'])
const hasAccess = allowed.has(permission)
6.5 并行异步操作
// ❌ 坏 - 顺序执行(总时间=时间之和)
const user = await fetchUser()
const metrics = await fetchMetrics()
// ✅ 好 - 并行执行(总时间=最大时间)
const [user, metrics] = await Promise.all([fetchUser(), fetchMetrics()])
// ✅ 好 - 带错误处理
const results = await Promise.allSettled([fetchUser(), fetchMetrics()])
7. 安全标准
7.1 已知漏洞
TypeScript本身有强大的安全记录。主要风险来自:
| 风险领域 | 描述 | 缓解措施 |
|---|---|---|
| 类型擦除 | 类型在运行时不存在 | 使用Zod进行外部数据验证 |
| 任意类型 | 禁用类型检查 | 启用noImplicitAny,使用unknown |
| 类型断言 | 可绕过类型系统 | 避免as,使用类型守卫 |
7.2 OWASP Top 10覆盖
| OWASP类别 | TypeScript缓解措施 |
|---|---|
| A03 注入 | 类型化API防止查询中的字符串插值 |
| A04 不安全设计 | 强类型化强制执行安全接口 |
| A08 软件完整性 | 编译时检查在部署前捕获错误 |
7.3 安全类型模式
// ❌ 危险 - 类型断言绕过安全性
const userData = apiResponse as User
// ✅ 安全 - 运行时验证
const userData = userSchema.parse(apiResponse)
// ❌ 危险 - any禁用所有检查
function process(data: any) { /* ... */ }
// ✅ 安全 - unknown需要缩小范围
function process(data: unknown) {
const validated = commandSchema.parse(data)
// 现在安全类型化
}
8. 测试与质量
// 使用vitest进行类型测试
import { expectTypeOf } from 'vitest'
describe('类型安全', () => {
it('应强制标记类型', () => {
expectTypeOf(createUserId('usr_1234567890123456')).toEqualTypeOf<UserId>()
})
})
// 模式验证测试
describe('命令模式', () => {
it('应拒绝无效ID', () => {
expect(() => commandSchema.parse({ id: 'invalid' })).toThrow()
})
it('应接受有效命令', () => {
const result = commandSchema.parse({ id: 'cmd_1234567890123456', action: 'query' })
expect(result.action).toBe('query')
})
})
9. 常见错误和反模式
9.1 关键安全反模式
绝不:使用类型断言处理外部数据
// ❌ 危险 - 无运行时验证
const user = JSON.parse(data) as User
// ✅ 安全 - 在运行时验证
const user = userSchema.parse(JSON.parse(data))
绝不:忽略空/未定义检查
// ❌ 危险 - 如果未定义则运行时崩溃
function getConfig(key: string) {
return config[key].value // 可能未定义!
}
// ✅ 安全 - 显式空处理
function getConfig(key: string): string | undefined {
return config[key]?.value
}
绝不:使用索引签名而不检查
// ❌ 危险 - 无类型安全
const handlers: Record<string, Handler> = {}
handlers['cmd'].execute() // 可能未定义!
// ✅ 安全 - 显式查找带检查
const handler = handlers['cmd']
if (handler) {
handler.execute()
}
9.2 性能反模式
避免:过度类型复杂性
// ❌ 坏 - 不可读,编译慢
type DeepPartialRecord<T> = T extends object
? { [K in keyof T]?: DeepPartialRecord<T[K]> }
: T extends Array<infer U>
? Array<DeepPartialRecord<U>>
: T
// ✅ 好 - 简单,清晰类型
interface PartialConfig {
theme?: Partial<ThemeConfig>
metrics?: Partial<MetricsConfig>
}
10. 预部署清单
阶段1: 写代码前
- [ ] 先写失败测试(测试驱动开发)
- [ ] 为新功能定义类型和接口
- [ ] 规划外部数据的Zod模式
- [ ] 识别性能关键路径
- [ ] 查看代码库中的现有模式
阶段2: 实现期间
- [ ] 在tsconfig中启用
strict: true - [ ] 启用
noUncheckedIndexedAccess: true - [ ] 启用
noImplicitAny: true - [ ] 生产代码中无
any类型 - [ ] 所有外部数据用Zod验证
- [ ] 敏感ID使用标记类型
- [ ] 状态机使用判别联合类型
- [ ] 昂贵计算应用记忆化
- [ ] 重型组件使用懒加载
- [ ] 查找使用高效数据结构(Map/Set)
阶段3: 提交前
- [ ] 所有测试通过(
npx vitest run) - [ ] 无代码检查错误(
npx eslint .) - [ ] 无TypeScript错误(
npx tsc --noEmit) - [ ] 所有API输入在运行时验证
- [ ] 外部数据无类型断言
- [ ] 应用性能模式于需要处
11. 总结
TypeScript为JARVIS开发提供编译时安全性:
- 严格配置: 启用所有严格选项以最大化安全性
- 运行时验证: 类型在运行时消失 - 使用Zod
- 标记类型: 防止混合ID和敏感值
- 类型守卫: 使用谓词安全缩小
unknown数据范围
记住: TypeScript仅在编译时检查。与外部数据的每个边界必须有运行时验证。
参考文献:
references/advanced-patterns.md- 复杂类型模式references/security-examples.md- 安全类型化实践