一键导入
typescript-patterns
Practical TypeScript patterns. Activated when working with type inference, utility types, generics, type guards, or type narrowing.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Practical TypeScript patterns. Activated when working with type inference, utility types, generics, type guards, or type narrowing.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
AGENTS.md authoring patterns. Activated when writing AI agent context files, working with agents.md or AGENTS.md.
Codebase onboarding guide. Activated when starting a new project analysis, requesting systematic codebase understanding, or running /onboard.
Feature-Sliced Design architecture patterns. Activated when working with FSD layers, slices, dependency rules, or structuring frontend code.
Modern React 19+ patterns. Activated when working with state management, Suspense, Compound Components, or React hooks.
React Query server state management patterns. Activated when applying Query Factory, queryOptions, or mutationOptions patterns.
Writing readable, maintainable code. Activated when applying naming conventions, function decomposition, or Early Return patterns.
| name | typescript-patterns |
| description | Practical TypeScript patterns. Activated when working with type inference, utility types, generics, type guards, or type narrowing. |
Practical type authoring patterns
// AVOID: unnecessary type annotations
const name: string = 'John';
const items: string[] = ['a', 'b', 'c'];
// PREFER: let inference work
const name = 'John';
const items = ['a', 'b', 'c'];
When to annotate: function parameters, complex return types, exported types
type UpdateUser = Partial<User>; // All optional
type UserPreview = Pick<User, 'id' | 'name'>; // Pick subset
type CreateUser = Omit<User, 'id' | 'createdAt'>; // Exclude fields
type RequiredConfig = Required<Config>; // All required
type UserMap = Record<string, User>; // Key-value map
// Combinations
type UpdateUser = Partial<User> & { id: string }; // id required, rest optional
type CreateUser = Omit<User, 'id'> & { id?: string }; // specific field optional
function process(input: string | number) {
if (typeof input === 'string') {
return input.toUpperCase();
}
return input.toFixed(2);
}
function handleResponse(res: SuccessResponse | ErrorResponse) {
if ('error' in res) {
console.error(res.error);
return;
}
return res.data;
}
type Result =
| { status: 'success'; data: User }
| { status: 'error'; error: Error }
| { status: 'loading' };
function handleResult(result: Result) {
switch (result.status) {
case 'success':
return result.data;
case 'error':
throw result.error;
case 'loading':
return null;
}
}
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value
);
}
const data: unknown = await fetchData();
if (isUser(data)) {
console.log(data.name);
}
// API response wrapper
interface ApiResponse<T> {
data: T;
status: number;
}
// Generic constraint
function findById<T extends { id: string }>(items: T[], id: string): T | undefined {
return items.find(item => item.id === id);
}
// Component generic
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
keyExtractor: (item: T) => string;
}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
return (
<ul>
{items.map(item => (
<li key={keyExtractor(item)}>{renderItem(item)}</li>
))}
</ul>
);
}
const ROUTES = {
HOME: '/',
ABOUT: '/about',
USER: '/user',
} as const;
type Route = typeof ROUTES[keyof typeof ROUTES]; // '/' | '/about' | '/user'
const STATUSES = ['pending', 'active', 'done'] as const;
type Status = typeof STATUSES[number]; // 'pending' | 'active' | 'done'
// AVOID: any
function process(data: any) { ... }
// USE: unknown + type guard
function process(data: unknown) {
if (isValidData(data)) { ... }
}
// AVOID: type assertion abuse
const user = data as User;
// USE: type guard
if (isUser(data)) { const user = data; }
// AVOID: non-null assertion overuse
const name = user!.name!;
// USE: optional chaining + nullish coalescing
const name = user?.name ?? 'Unknown';
// AVOID: enum (not tree-shakeable)
enum Status { Active, Inactive }
// USE: as const + union
const Status = { Active: 'active', Inactive: 'inactive' } as const;
type Status = typeof Status[keyof typeof Status];
// Component Props type
type ButtonProps = React.ComponentProps<'button'> & {
variant?: 'primary' | 'secondary';
};
// Event handler type
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
setValue(e.target.value);
};
// Promise return type
async function fetchUser(id: string): Promise<User> {
const response = await api.get(`/users/${id}`);
return response.data;
}
| Principle | Description |
|---|---|
| Types are docs | Express intent clearly, don't overcomplicate |
| Infer first | Only annotate when inference falls short |
| unknown > any | Always prefer unknown with guards |