关于 typescript-best-practices
面向编码 Agent 的 TypeScript 技能,侧重类型优先设计、运行时验证和语言习惯。处理 React 文件时,建议与 `react-best-practices` 配合使用。
主要功能
- 01类型优先建模
- 02判别联合与品牌类型
- 03使用 Zod 进行运行时验证
- 04穷尽式 switch 检查
- 05可选的 type-fest 工具
适用场景
- 01建模以排除无效状态
- 02验证并规范化运行时输入
- 03保持 Schema 与推断类型一致
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ alleneubank/ claude-code --skill typescript-best-practices安装后在 Claude Code 中输入/typescript-best-practices确认已加载。
详细介绍
TypeScript 最佳实践
遵循 CLAUDE.md 中以类型优先、函数式和错误处理为核心的模式。本技能仅涵盖语言特定的惯用写法。
与 React 最佳实践搭配使用
处理 React 组件(.tsx、.jsx 文件或 @react 导入)时,始终同时加载 react-best-practices。本技能涵盖 TypeScript 基础知识;React 专属模式(副作用、钩子、引用、组件设计)请参阅专门的 React 技能。
让非法状态无法表示
使用类型系统在编译时阻止无效状态。
互斥状态使用判别联合类型:
// Good: only valid combinations possible
type RequestState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error };
// Bad: allows invalid combinations like { loading: true, error: Error }
type RequestState<T> = {
loading: boolean;
data?: T;
error?: Error;
};
领域原始类型使用品牌类型:
type UserId = string & { readonly __brand: 'UserId' };
type OrderId = string & { readonly __brand: 'OrderId' };
// Compiler prevents passing OrderId where UserId expected
function getUser(id: UserId): Promise<User> { /* ... */ }
字面量联合类型使用 const 断言:
const ROLES = ['admin', 'user', 'guest'] as const;
type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest'
// Array and type stay in sync automatically
function isValidRole(role: string): role is Role {
return ROLES.includes(role as Role);
}
使用 never 检查穷尽式 switch:
type Status = "active" | "inactive";
function processStatus(status: Status): string {
switch (status) {
case "active":
return "processing";
case "inactive":
return "skipped";
default: {
const _exhaustive: never = status;
throw new Error(`unhandled status: ${_exhaustive}`);
}
}
}
使用 Zod 进行运行时验证
- 将架构定义为唯一事实来源;使用
z.infer<>推导 TypeScript 类型。避免重复定义类型和架构。 - 对于预期可能失败的用户输入,使用
safeParse;对于无效数据意味着错误的信任边界,使用parse。 - 使用
.extend()、.pick()、.omit()、.merge()组合架构,遵循 DRY 原则。 - 添加
.transform(),在解析时规范化数据(修剪字符串、解析日期)。
import { z } from "zod";
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1),
createdAt: z.string().transform((s) => new Date(s)),
});
type User = z.infer<typeof UserSchema>;
// Strict parsing at trust boundaries — throws if API contract violated
export async function fetchUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`fetch user ${id} failed: ${response.status}`);
}
return UserSchema.parse(await response.json());
}
// Caller handles both success and error from user input
const result = UserSchema.safeParse(formData);
if (!result.success) {
setErrors(result.error.flatten().fieldErrors);
return;
}
可选:type-fest
对于超出 TypeScript 内置类型工具范围的高级类型工具,可考虑使用 type-fest:
Opaque<T, Token>- 比手动使用& { __brand }模式更简洁的品牌类型PartialDeep<T>- 为嵌套对象递归设置为可选ReadonlyDeep<T>- 为不可变数据递归设置为只读SetRequired<T, K>/SetOptional<T, K>- 定向修改字段Simplify<T>- 在 IDE 工具提示中展平复杂的交叉类型
import type { Opaque, PartialDeep } from 'type-fest';
type UserId = Opaque<string, 'UserId'>;
type UserPatch = PartialDeep<User>;
评论
支持 Markdown