RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Configs/Cursor rules/crunl/Xingyu-Frontend

Cursor rule

.cursor/rules/typescript.mdc

[object Object]

Cursor rules

Quality

42/100

Scores the file, not the repository.

Length

2,841 words

25 headings · 17 code blocks

Repository

0

— · pushed 377 days ago

Last changed

3 days ago

First indexed 3 days ago.
crunl/Xingyu-Frontend/.cursor/rules/typescript.mdcRawGitHub
1---
2description:
3globs:
4alwaysApply: false
5---
6# SoybeanAdmin React TypeScript 规范
7 
8## 概述
9 
10本文档定义了 SoybeanAdmin React 项目的 TypeScript 使用规范,旨在确保类型安全、提高代码质量和团队协作效率。严格遵循这些规范将帮助我们构建类型安全、可维护的应用程序。
11 
12## 基本原则
13 
14### 🎯 核心原则
15 
161. **类型安全优先**:所有组件和函数必须提供准确的类型定义
172. **避免类型逃逸**:禁止使用 `any` 类型,必要时使用 `unknown`
183. **明确胜过隐式**:显式声明类型比依赖推断更可靠
194. **一致性标准**:使用统一的类型定义和命名约定
205. **编译零错误**:确保编译无任何类型错误或警告
21 
22### ⚠️ 严格规则
23 
24```typescript
25// ✅ 正确做法
26// 1. 禁止使用 any,必要时使用 unknown
27function processData(data: unknown): ProcessedData {
28 if (typeof data === 'object' && data !== null) {
29 return data as ProcessedData;
30 }
31 throw new Error('Invalid data format');
32}
33 
34// 2. 所有导出类型统一从 src/types 出口引入
35import type { UserInfo, ApiResponse } from '@/types';
36 
37// 3. 枚举优先使用 const enum
38const enum UserRole {
39 Admin = 'admin',
40 User = 'user',
41 Guest = 'guest'
42}
43 
44// 4. 函数参数与返回值必须明确声明类型
45function calculateScore(user: UserInfo, factors: ScoreFactor[]): number {
46 // 实现逻辑
47 return 0;
48}
49 
50// 5. 类型别名使用 type,对象结构使用 interface
51type Theme = 'light' | 'dark' | 'auto';
52 
53interface UserInfo {
54 id: string;
55 name: string;
56 role: UserRole;
57}
58 
59// ❌ 错误做法
60function processData(data: any): any { // 禁止使用 any
61 return data;
62}
63 
64enum UserRole { // 不推荐使用普通 enum
65 Admin = 'admin',
66 User = 'user'
67}
68 
69function calculate(user, factors) { // 缺少类型声明
70 return 0;
71}
72```
73 
74## 组件类型定义
75 
76### 🧩 React 组件规范
77 
78```typescript
79// ✅ 正确示例
80/**
81 * 用户信息卡片组件属性
82 * @description 定义用户卡片组件的所有属性类型
83 */
84interface UserCardProps {
85 /** 用户信息对象 */
86 user: UserInfo;
87 /** 卡片尺寸 */
88 size?: 'small' | 'medium' | 'large';
89 /** 是否显示操作按钮 */
90 showActions?: boolean;
91 /** 自定义样式类名 */
92 className?: string;
93 /** 点击编辑时的回调函数 */
94 onEdit?: (user: UserInfo) => void;
95 /** 点击删除时的回调函数 */
96 onDelete?: (userId: string) => Promise<void>;
97 /** 自定义渲染函数 */
98 renderExtra?: (user: UserInfo) => React.ReactNode;
99}
100 
101/**
102 * 用户卡片组件状态
103 */
104interface UserCardState {
105 isLoading: boolean;
106 error: string | null;
107 isExpanded: boolean;
108}
109 
110/**
111 * 用户信息卡片组件
112 * @param props 组件属性
113 * @returns JSX 元素
114 */
115const UserCard: React.FC<UserCardProps> = ({
116 user,
117 size = 'medium',
118 showActions = true,
119 className,
120 onEdit,
121 onDelete,
122 renderExtra
123}) => {
124 const [state, setState] = useState<UserCardState>({
125 isLoading: false,
126 error: null,
127 isExpanded: false
128 });
129 
130 // 处理编辑操作
131 const handleEdit = useCallback((): void => {
132 onEdit?.(user);
133 }, [user, onEdit]);
134 
135 // 处理删除操作
136 const handleDelete = useCallback(async (): Promise<void> => {
137 if (!onDelete) return;
138 
139 setState(prev => ({ ...prev, isLoading: true, error: null }));
140 
141 try {
142 await onDelete(user.id);
143 } catch (error) {
144 setState(prev => ({
145 ...prev,
146 error: error instanceof Error ? error.message : '删除失败'
147 }));
148 } finally {
149 setState(prev => ({ ...prev, isLoading: false }));
150 }
151 }, [user.id, onDelete]);
152 
153 return (
154 <div className={clsx('user-card', `user-card--${size}`, className)}>
155 {/* 组件实现 */}
156 </div>
157 );
158};
159 
160export default UserCard;
161 
162// 导出组件类型供外部使用
163export type { UserCardProps, UserCardState };
164```
165 
166 
167 
168## 泛型使用规范
169 
170### 🔗 泛型最佳实践
171 
172```typescript
173// ✅ 正确示例
174/**
175 * 通用表格组件属性
176 * @template T 表格数据项的类型
177 */
178interface TableProps<T extends Record<string, any> = Record<string, any>> {
179 /** 表格数据 */
180 data: T[];
181 /** 表格列配置 */
182 columns: TableColumn<T>[];
183 /** 是否加载中 */
184 loading?: boolean;
185 /** 行点击事件 */
186 onRowClick?: (record: T, index: number) => void;
187 /** 行选择事件 */
188 onSelectionChange?: (selectedRows: T[]) => void;
189}
190 
191/**
192 * 表格列配置
193 * @template T 数据项类型
194 */
195interface TableColumn<T> {
196 /** 列标题 */
197 title: string;
198 /** 数据字段键 */
199 dataIndex: keyof T;
200 /** 列宽度 */
201 width?: number;
202 /** 自定义渲染函数 */
203 render?: (value: T[keyof T], record: T, index: number) => React.ReactNode;
204 /** 排序配置 */
205 sorter?: boolean | ((a: T, b: T) => number);
206}
207 
208/**
209 * API 响应数据结构
210 * @template T 响应数据类型
211 */
212interface ApiResponse<T = any> {
213 /** 状态码 */
214 code: number;
215 /** 响应消息 */
216 message: string;
217 /** 响应数据 */
218 data: T;
219 /** 请求是否成功 */
220 success: boolean;
221 /** 时间戳 */
222 timestamp: number;
223}
224 
225/**
226 * 分页响应数据
227 * @template T 列表项类型
228 */
229interface PaginatedResponse<T> {
230 /** 数据列表 */
231 list: T[];
232 /** 总数量 */
233 total: number;
234 /** 当前页码 */
235 page: number;
236 /** 每页数量 */
237 pageSize: number;
238 /** 总页数 */
239 totalPages: number;
240}
241 
242/**
243 * 表单字段配置
244 * @template T 表单数据类型
245 */
246interface FormField<T extends Record<string, any>> {
247 /** 字段名 */
248 name: keyof T;
249 /** 字段标签 */
250 label: string;
251 /** 字段类型 */
252 type: 'input' | 'select' | 'textarea' | 'number' | 'date';
253 /** 是否必填 */
254 required?: boolean;
255 /** 验证规则 */
256 validator?: (value: T[keyof T]) => string | undefined;
257 /** 字段选项(用于 select 类型) */
258 options?: Array<{ label: string; value: T[keyof T] }>;
259}
260 
261// 使用泛型的实际示例
262const UserTable: React.FC<TableProps<UserInfo>> = ({ data, columns, loading, onRowClick }) => {
263 // 实现逻辑
264 return <div>User Table</div>;
265};
266 
267// 为复杂泛型提供类型别名
268type UserTableProps = TableProps<UserInfo>;
269type UserApiResponse = ApiResponse<UserInfo>;
270type UserListResponse = ApiResponse<PaginatedResponse<UserInfo>>;
271```
272 
273### 🔒 泛型约束
274 
275```typescript
276// ✅ 正确示例
277/**
278 * 确保泛型 T 包含 id 属性
279 */
280interface HasId {
281 id: string;
282}
283 
284/**
285 * 通用删除函数
286 * @template T 必须包含 id 属性的类型
287 */
288function deleteItem<T extends HasId>(item: T): Promise<void> {
289 return fetch(`/api/items/${item.id}`, { method: 'DELETE' }).then();
290}
291 
292/**
293 * 键值对类型约束
294 * @template K 键类型,必须是字符串
295 * @template V 值类型
296 */
297interface KeyValuePair<K extends string, V> {
298 key: K;
299 value: V;
300}
301 
302/**
303 * 确保对象类型约束
304 * @template T 必须是对象类型
305 */
306function cloneObject<T extends Record<string, any>>(obj: T): T {
307 return { ...obj };
308}
309 
310/**
311 * React 组件 Props 约束
312 * @template P 组件 Props 类型
313 */
314type ComponentWithProps<P extends Record<string, any>> = React.FC<P>;
315```
316 
317## 类型合并与扩展
318 
319### 🔀 交叉类型和联合类型
320 
321```typescript
322// ✅ 正确示例
323/**
324 * 基础用户信息
325 */
326interface BaseUser {
327 id: string;
328 name: string;
329 email: string;
330}
331 
332/**
333 * 用户权限信息
334 */
335interface UserPermissions {
336 role: UserRole;
337 permissions: string[];
338 canEdit: boolean;
339 canDelete: boolean;
340}
341 
342/**
343 * 用户活动信息
344 */
345interface UserActivity {
346 lastLogin: string;
347 loginCount: number;
348 isActive: boolean;
349}
350 
351// 使用交叉类型合并多个接口
352type FullUserInfo = BaseUser & UserPermissions & UserActivity;
353 
354// 使用工具类型修改现有类型
355type PartialUser = Partial<BaseUser>; // 所有属性可选
356type UserNameAndEmail = Pick<BaseUser, 'name' | 'email'>; // 只选择特定属性
357type UserWithoutId = Omit<BaseUser, 'id'>; // 排除特定属性
358type RequiredUser = Required<Partial<BaseUser>>; // 所有属性必填
359 
360/**
361 * 扩展 HTML 元素属性
362 */
363interface CustomButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
364 /** 按钮变体 */
365 variant?: 'primary' | 'secondary' | 'danger';
366 /** 按钮尺寸 */
367 size?: 'small' | 'medium' | 'large';
368 /** 是否加载中 */
369 loading?: boolean;
370 /** 图标组件 */
371 icon?: React.ReactNode;
372}
373 
374/**
375 * 条件类型示例
376 */
377type ApiResponseType<T> = T extends string
378 ? { message: T }
379 : T extends number
380 ? { code: T }
381 : { data: T };
382 
383/**
384 * 映射类型示例
385 */
386type ReadonlyUser = {
387 readonly [K in keyof BaseUser]: BaseUser[K];
388};
389 
390type OptionalUser = {
391 [K in keyof BaseUser]?: BaseUser[K];
392};
393```
394 
395## 枚举和常量
396 
397### 📝 枚举使用规范
398 
399```typescript
400// ✅ 推荐:使用 const enum 和联合类型
401/**
402 * 用户角色枚举
403 */
404const enum UserRole {
405 Admin = 'admin',
406 User = 'user',
407 Guest = 'guest',
408 SuperAdmin = 'super_admin'
409}
410 
411/**
412 * 请求状态枚举
413 */
414const enum RequestStatus {
415 Idle = 'idle',
416 Loading = 'loading',
417 Success = 'success',
418 Error = 'error'
419}
420 
421// 使用联合类型和 as const
422const THEME_MODES = ['light', 'dark', 'auto'] as const;
423type ThemeMode = typeof THEME_MODES[number]; // 'light' | 'dark' | 'auto'
424 
425const HTTP_STATUS_CODES = {
426 OK: 200,
427 NOT_FOUND: 404,
428 UNAUTHORIZED: 401,
429 FORBIDDEN: 403,
430 INTERNAL_ERROR: 500
431} as const;
432 
433type HttpStatusCode = typeof HTTP_STATUS_CODES[keyof typeof HTTP_STATUS_CODES];
434 
435/**
436 * 复杂常量配置
437 */
438const APP_CONFIG = {
439 api: {
440 baseURL: 'https://api.example.com',
441 timeout: 10000,
442 retryCount: 3
443 },
444 ui: {
445 defaultPageSize: 20,
446 maxPageSize: 100,
447 themes: THEME_MODES
448 },
449 features: {
450 enableDarkMode: true,
451 enableI18n: true,
452 enablePWA: false
453 }
454} as const;
455 
456type AppConfig = typeof APP_CONFIG;
457 
458// 类型守卫函数
459/**
460 * 检查是否为有效的用户角色
461 */
462function isValidUserRole(role: string): role is UserRole {
463 return Object.values(UserRole).includes(role as UserRole);
464}
465 
466/**
467 * 检查是否为有效的主题模式
468 */
469function isValidThemeMode(mode: string): mode is ThemeMode {
470 return THEME_MODES.includes(mode as ThemeMode);
471}
472 
473// ❌ 不推荐:使用普通 enum
474enum BadExample {
475 Value1 = 'value1',
476 Value2 = 'value2'
477}
478```
479 
480## 类型推断与断言
481 
482### 🔍 类型守卫和断言
483 
484```typescript
485// ✅ 正确示例
486/**
487 * 类型谓词函数 - 检查是否为用户对象
488 */
489function isUser(obj: unknown): obj is UserInfo {
490 return (
491 typeof obj === 'object' &&
492 obj !== null &&
493 'id' in obj &&
494 'name' in obj &&
495 'email' in obj &&
496 typeof (obj as any).id === 'string' &&
497 typeof (obj as any).name === 'string' &&
498 typeof (obj as any).email === 'string'
499 );
500}
501 
502/**
503 * 类型守卫 - 检查错误对象
504 */
505function isApiError(error: unknown): error is ApiError {
506 return (
507 error instanceof Error &&
508 'code' in error &&
509 'status' in error &&
510 typeof (error as any).code === 'number'
511 );
512}
513 
514/**
515 * 使用 typeof 进行类型守卫
516 */
517function processValue(value: string | number): string {
518 if (typeof value === 'string') {
519 // TypeScript 知道这里 value 是 string 类型
520 return value.toUpperCase();
521 }
522 
523 // TypeScript 知道这里 value 是 number 类型
524 return value.toString();
525}
526 
527/**
528 * 使用 instanceof 进行类型守卫
529 */
530function handleError(error: Error | string): void {
531 if (error instanceof Error) {
532 // error 是 Error 类型
533 console.error('Error name:', error.name);
534 console.error('Error message:', error.message);
535 console.error('Error stack:', error.stack);
536 } else {
537 // error 是 string 类型
538 console.error('Error message:', error);
539 }
540}
541 
542/**
543 * 安全的类型断言
544 */
545function processApiResponse(response: unknown): UserInfo[] {
546 // 先进行类型检查,再进行断言
547 if (
548 typeof response === 'object' &&
549 response !== null &&
550 'data' in response &&
551 Array.isArray((response as any).data)
552 ) {
553 const data = (response as { data: unknown[] }).data;
554 
555 // 进一步验证数组元素
556 if (data.every(isUser)) {
557 return data; // TypeScript 推断为 UserInfo[]
558 }
559 }
560 
561 throw new Error('Invalid API response format');
562}
563 
564/**
565 * 条件类型推断
566 */
567type ExtractArrayType<T> = T extends (infer U)[] ? U : never;
568type StringArrayType = ExtractArrayType<string[]>; // string
569type NumberArrayType = ExtractArrayType<number[]>; // number
570 
571/**
572 * 工具函数 - 安全获取对象属性
573 */
574function safeGet<T extends Record<string, any>, K extends keyof T>(
575 obj: T,
576 key: K
577): T[K] | undefined {
578 return obj && typeof obj === 'object' ? obj[key] : undefined;
579}
580 
581// ❌ 避免的做法
582function badExample(data: unknown): UserInfo {
583 return data as UserInfo; // 危险的强制断言
584}
585 
586function alsobad(user: UserInfo): string {
587 return (user as any).someProperty; // 使用 any 绕过类型检查
588}
589```
590 
591## Hook 类型定义
592 
593### 🪝 自定义 Hook 规范
594 
595```typescript
596// ✅ 正确示例
597/**
598 * 用户信息管理 Hook 的返回类型
599 */
600interface UseUserInfoReturn {
601 /** 用户信息 */
602 user: UserInfo | null;
603 /** 加载状态 */
604 loading: boolean;
605 /** 错误信息 */
606 error: string | null;
607 /** 刷新用户信息 */
608 refresh: () => Promise<void>;
609 /** 更新用户信息 */
610 updateUser: (updates: Partial<UserInfo>) => Promise<void>;
611}
612 
613/**
614 * 用户信息管理 Hook
615 * @param userId 用户ID
616 * @returns 用户信息和相关操作方法
617 */
618function useUserInfo(userId: string): UseUserInfoReturn {
619 const [user, setUser] = useState<UserInfo | null>(null);
620 const [loading, setLoading] = useState<boolean>(false);
621 const [error, setError] = useState<string | null>(null);
622 
623 const fetchUser = useCallback(async (): Promise<void> => {
624 if (!userId) return;
625 
626 setLoading(true);
627 setError(null);
628 
629 try {
630 const userData = await fetchUserById(userId);
631 setUser(userData);
632 } catch (err) {
633 const errorMessage = err instanceof Error ? err.message : '获取用户信息失败';
634 setError(errorMessage);
635 } finally {
636 setLoading(false);
637 }
638 }, [userId]);
639 
640 const updateUser = useCallback(async (updates: Partial<UserInfo>): Promise<void> => {
641 if (!user) return;
642 
643 try {
644 const updatedUser = await updateUserById(user.id, updates);
645 setUser(updatedUser);
646 } catch (err) {
647 const errorMessage = err instanceof Error ? err.message : '更新用户信息失败';
648 setError(errorMessage);
649 throw err;
650 }
651 }, [user]);
652 
653 useEffect(() => {
654 fetchUser();
655 }, [fetchUser]);
656 
657 return {
658 user,
659 loading,
660 error,
661 refresh: fetchUser,
662 updateUser
663 };
664}
665 
666/**
667 * 表格数据管理 Hook
668 * @template T 表格数据项类型
669 */
670interface UseTableOptions<T> {
671 /** 数据获取函数 */
672 fetchData: (params: any) => Promise<PaginatedResponse<T>>;
673 /** 默认查询参数 */
674 defaultParams?: Record<string, any>;
675 /** 是否自动加载 */
676 autoLoad?: boolean;
677}
678 
679interface UseTableReturn<T> {
680 /** 表格数据 */
681 data: T[];
682 /** 加载状态 */
683 loading: boolean;
684 /** 错误信息 */
685 error: string | null;
686 /** 分页信息 */
687 pagination: {
688 current: number;
689 pageSize: number;
690 total: number;
691 };
692 /** 查询参数 */
693 params: Record<string, any>;
694 /** 设置查询参数 */
695 setParams: (newParams: Record<string, any>) => void;
696 /** 刷新数据 */
697 refresh: () => Promise<void>;
698 /** 重置到第一页 */
699 reset: () => void;
700}
701 
702function useTable<T extends Record<string, any>>(
703 options: UseTableOptions<T>
704): UseTableReturn<T> {
705 const { fetchData, defaultParams = {}, autoLoad = true } = options;
706 
707 const [data, setData] = useState<T[]>([]);
708 const [loading, setLoading] = useState<boolean>(false);
709 const [error, setError] = useState<string | null>(null);
710 const [pagination, setPagination] = useState({
711 current: 1,
712 pageSize: 20,
713 total: 0
714 });
715 const [params, setParams] = useState(defaultParams);
716 
717 const loadData = useCallback(async (): Promise<void> => {
718 setLoading(true);
719 setError(null);
720 
721 try {
722 const response = await fetchData({
723 ...params,
724 page: pagination.current,
725 pageSize: pagination.pageSize
726 });
727 
728 setData(response.list);
729 setPagination(prev => ({
730 ...prev,
731 total: response.total
732 }));
733 } catch (err) {
734 const errorMessage = err instanceof Error ? err.message : '加载数据失败';
735 setError(errorMessage);
736 } finally {
737 setLoading(false);
738 }
739 }, [fetchData, params, pagination.current, pagination.pageSize]);
740 
741 const handleSetParams = useCallback((newParams: Record<string, any>): void => {
742 setParams(newParams);
743 setPagination(prev => ({ ...prev, current: 1 }));
744 }, []);
745 
746 const reset = useCallback((): void => {
747 setParams(defaultParams);
748 setPagination(prev => ({ ...prev, current: 1 }));
749 }, [defaultParams]);
750 
751 useEffect(() => {
752 if (autoLoad) {
753 loadData();
754 }
755 }, [loadData, autoLoad]);
756 
757 return {
758 data,
759 loading,
760 error,
761 pagination,
762 params,
763 setParams: handleSetParams,
764 refresh: loadData,
765 reset
766 };
767}
768```
769 
770## JSDoc 注释规范
771 
772### 📚 TypeScript JSDoc
773 
774```typescript
775// ✅ 正确示例
776/**
777 * 用户服务类
778 * @description 提供用户相关的API操作和数据管理功能
779 * @since 1.0.0
780 * @author 张三 <zhangsan@example.com>
781 */
782class UserService {
783 private apiClient: ApiClient;
784 
785 /**
786 * 构造函数
787 * @param apiClient API客户端实例
788 */
789 constructor(apiClient: ApiClient) {
790 this.apiClient = apiClient;
791 }
792 
793 /**
794 * 获取用户列表
795 * @template T 用户数据类型,默认为 UserInfo
796 * @param params 查询参数
797 * @param params.page 页码,从1开始
798 * @param params.pageSize 每页数量,范围1-100
799 * @param params.keyword 搜索关键词,支持用户名和邮箱
800 * @returns Promise<PaginatedResponse<T>> 分页的用户列表
801 * @throws {ApiError} 当请求失败时抛出API错误
802 * @example
803 * ```typescript
804 * const userService = new UserService(apiClient);
805 * const users = await userService.getUserList({
806 * page: 1,
807 * pageSize: 20,
808 * keyword: 'admin'
809 * });
810 * ```
811 */
812 async getUserList<T extends UserInfo = UserInfo>(
813 params: UserListParams
814 ): Promise<PaginatedResponse<T>> {
815 const response = await this.apiClient.get<PaginatedResponse<T>>('/users', {
816 params
817 });
818 return response.data;
819 }
820 
821 /**
822 * 创建新用户
823 * @param userData 用户数据
824 * @param userData.name 用户名,长度2-50字符
825 * @param userData.email 邮箱地址,必须符合邮箱格式
826 * @param userData.role 用户角色,默认为'user'
827 * @returns Promise<UserInfo> 创建成功的用户信息
828 * @throws {ValidationError} 当数据验证失败时抛出验证错误
829 * @throws {ConflictError} 当邮箱已存在时抛出冲突错误
830 * @deprecated 使用 createUserV2 替代,将在 v2.0 版本中移除
831 */
832 async createUser(userData: CreateUserData): Promise<UserInfo> {
833 const response = await this.apiClient.post<UserInfo>('/users', userData);
834 return response.data;
835 }
836}
837 
838/**
839 * 格式化用户显示名称
840 * @param user 用户信息对象
841 * @param options 格式化选项
842 * @param options.showEmail 是否显示邮箱,默认false
843 * @param options.showRole 是否显示角色,默认false
844 * @returns 格式化后的显示名称
845 * @example
846 * ```typescript
847 * const user: UserInfo = { name: '张三', email: 'zhang@example.com', role: 'admin' };
848 *
849 * formatUserDisplayName(user) // '张三'
850 * formatUserDisplayName(user, { showEmail: true }) // '张三 (zhang@example.com)'
851 * formatUserDisplayName(user, { showRole: true }) // '张三 [admin]'
852 * ```
853 */
854function formatUserDisplayName(
855 user: UserInfo,
856 options: {
857 showEmail?: boolean;
858 showRole?: boolean;
859 } = {}
860): string {
861 let displayName = user.name;
862 
863 if (options.showEmail && user.email) {
864 displayName += ` (${user.email})`;
865 }
866 
867 if (options.showRole && user.role) {
868 displayName += ` [${user.role}]`;
869 }
870 
871 return displayName;
872}
873 
874/**
875 * 通用数据转换函数
876 * @template TInput 输入数据类型
877 * @template TOutput 输出数据类型
878 * @param data 输入数据
879 * @param transformer 转换函数
880 * @returns 转换后的数据
881 * @example
882 * ```typescript
883 * const users = [{ id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }];
884 * const userOptions = transformData(users, user => ({
885 * label: user.name,
886 * value: user.id
887 * }));
888 * ```
889 */
890function transformData<TInput, TOutput>(
891 data: TInput[],
892 transformer: (item: TInput, index: number) => TOutput
893): TOutput[] {
894 return data.map(transformer);
895}
896```
897 
898## 类型导出和模块化
899 
900### 📦 类型模块组织
901 
902```typescript
903// ✅ src/types/index.ts - 统一类型出口
904/**
905 * 用户相关类型
906 */
907export type { UserInfo, UserRole, CreateUserData, UpdateUserData } from './user';
908 
909/**
910 * API 相关类型
911 */
912export type { ApiResponse, ApiError, PaginatedResponse } from './api';
913 
914/**
915 * 组件相关类型
916 */
917export type { TableProps, TableColumn, FormField } from './components';
918 
919/**
920 * 应用配置类型
921 */
922export type { AppConfig, ThemeConfig, RouteConfig } from './config';
923 
924/**
925 * 工具类型
926 */
927export type { DeepPartial, DeepRequired, ValueOf, KeysOfType } from './utils';
928 
929// ✅ src/types/user.ts
930/**
931 * 用户角色枚举
932 */
933export const enum UserRole {
934 Admin = 'admin',
935 User = 'user',
936 Guest = 'guest'
937}
938 
939/**
940 * 用户信息接口
941 */
942export interface UserInfo {
943 /** 用户唯一标识 */
944 id: string;
945 /** 用户名 */
946 name: string;
947 /** 邮箱地址 */
948 email: string;
949 /** 用户角色 */
950 role: UserRole;
951 /** 头像URL */
952 avatar?: string;
953 /** 创建时间 */
954 createTime: string;
955 /** 更新时间 */
956 updateTime: string;
957 /** 是否激活 */
958 isActive: boolean;
959}
960 
961/**
962 * 创建用户数据
963 */
964export interface CreateUserData {
965 name: string;
966 email: string;
967 role?: UserRole;
968 avatar?: string;
969}
970 
971/**
972 * 更新用户数据
973 */
974export type UpdateUserData = Partial<Omit<UserInfo, 'id' | 'createTime' | 'updateTime'>>;
975 
976// ✅ src/types/utils.ts - 工具类型
977/**
978 * 深度可选类型
979 */
980export type DeepPartial<T> = {
981 [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
982};
983 
984/**
985 * 深度必需类型
986 */
987export type DeepRequired<T> = {
988 [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
989};
990 
991/**
992 * 获取对象值的联合类型
993 */
994export type ValueOf<T> = T[keyof T];
995 
996/**
997 * 获取指定类型的键
998 */
999export type KeysOfType<T, U> = {
1000 [K in keyof T]: T[K] extends U ? K : never;
1001}[keyof T];
1002 
1003/**
1004 * 条件类型 - 如果 T 是 U 的子类型则返回 X,否则返回 Y
1005 */
1006export type If<T extends U, U, X, Y> = T extends U ? X : Y;
1007 
1008/**
1009 * 函数参数类型
1010 */
1011export type FunctionArgs<T> = T extends (...args: infer A) => any ? A : never;
1012 
1013/**
1014 * 函数返回值类型
1015 */
1016export type FunctionReturn<T> = T extends (...args: any[]) => infer R ? R : never;
1017```
1018 
1019 
1020## 最佳实践总结
1021 
1022### ✅ 推荐做法
1023 
10241. **使用统一的类型导入**
1025```typescript
1026// ✅ 正确
1027import type { UserInfo, ApiResponse } from '@/types';
1028 
1029// ❌ 错误
1030import { UserInfo } from '../types/user';
1031import { ApiResponse } from '../../types/api';
1032```
1033 
10342. **明确的函数签名**
1035```typescript
1036// ✅ 正确
1037function processUsers(users: UserInfo[]): ProcessedUser[] {
1038 return users.map(transformUser);
1039}
1040 
1041// ❌ 错误
1042function processUsers(users: any): any {
1043 return users.map(transformUser);
1044}
1045```
1046 
10473. **使用类型守卫而非断言**
1048```typescript
1049// ✅ 正确
1050if (isUserInfo(data)) {
1051 console.log(data.name); // TypeScript 知道 data 是 UserInfo
1052}
1053 
1054// ❌ 错误
1055console.log((data as UserInfo).name); // 危险的断言
1056```
1057 
10584. **导出组件类型**
1059```typescript
1060// ✅ 正确
1061export default UserCard;
1062export type { UserCardProps };
1063 
1064// ❌ 错误
1065export default UserCard;
1066// 没有导出 Props 类型
1067```
1068 
1069### 📋 代码审查检查清单
1070 
1071- [ ] 所有函数都有明确的参数和返回值类型
1072- [ ] 没有使用 `any` 类型
1073- [ ] 所有接口和类型都有 JSDoc 注释
1074- [ ] 使用了合适的泛型约束
1075- [ ] 导出了所有公共类型
1076- [ ] 使用了统一的类型导入路径
1077- [ ] 枚举使用了 `const enum`
1078- [ ] 复杂类型有类型守卫函数
1079- [ ] 编译没有任何错误或警告
1080 
1081遵循这些 TypeScript 规范将确保项目具有良好的类型安全性、可维护性和开发体验!
1082 

Sections

  • SoybeanAdmin React TypeScript 规范
  • 概述
  • 基本原则
  • 🎯 核心原则
  • ⚠️ 严格规则
  • 组件类型定义
  • 🧩 React 组件规范
  • 泛型使用规范
  • 🔗 泛型最佳实践
  • 🔒 泛型约束
  • 类型合并与扩展
  • 🔀 交叉类型和联合类型
  • 枚举和常量
  • 📝 枚举使用规范
  • 类型推断与断言
  • 🔍 类型守卫和断言
  • Hook 类型定义
  • 🪝 自定义 Hook 规范
  • JSDoc 注释规范
  • 📚 TypeScript JSDoc
  • 类型导出和模块化
  • 📦 类型模块组织
  • 最佳实践总结
  • ✅ 推荐做法
  • 📋 代码审查检查清单

What it covers

typesdocs

Stack — with the evidence

typescript

(1.00)

vite

(1.00)

eslint

(1.00)

node

(0.70)

react

(0.70)

javascript

(0.60)

monorepo

(0.60)

pnpm

(0.60)

github-actions

(0.60)

vercel

(0.60)

Glob targeting

  • [object Object]

Format

Cursor rules

The most expressive format here. Many small .mdc files, each with frontmatter declaring when it should load, so a rule about migrations only enters context when a migration is open. Costs the most to maintain and only one editor reads it.

What the corpus says about it

Repository

Owner
crunl
Language
—
License
—
Archived
no

All configs in this repo

Also in crunl/Xingyu-Frontend

Diff this repo’s formats

One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?

The other instruction files in this repository
RepositoryFormatStackCoversScoreChanged
crunl/Xingyu-Frontend.cursor/rules/api.mdc · 0Cursor rulestypescriptvite+8api45/1003 days ago
crunl/Xingyu-Frontend.cursor/rules/comments.mdc · 0Cursor rulestypescriptvite+8securityapidocs46/1003 days ago
crunl/Xingyu-Frontend.cursor/rules/componenting.mdc · 0Cursor rulestypescriptvite+8no sections45/1003 days ago
crunl/Xingyu-Frontend.cursor/rules/naming.mdc · 0Cursor rulestypescriptvite+8archtypesapiui58/1003 days ago
crunl/Xingyu-Frontend.cursor/rules/project.mdc · 0Cursor rulestypescriptvite+8no sections50/1003 days ago
crunl/Xingyu-Frontend.cursor/rules/reduxing.mdc · 0Cursor rulestypescriptvite+8no sections45/1003 days ago
crunl/Xingyu-Frontend.cursor/rules/routing.mdc · 0Cursor rulestypescriptvite+8no sections53/1003 days ago
crunl/Xingyu-Frontend.cursor/rules/styling.mdc · 0Cursor rulestypescriptvite+8archui54/1003 days ago
Diff against .cursor/rules/api.mdc Diff against .cursor/rules/comments.mdc Diff against .cursor/rules/componenting.mdc Diff against .cursor/rules/naming.mdc Diff against .cursor/rules/project.mdc Diff against .cursor/rules/reduxing.mdc Diff against .cursor/rules/routing.mdc Diff against .cursor/rules/styling.mdc

Similar configs

Same format, overlapping stack, ranked by quality.

Same format, overlapping stack, ranked by quality
RepositoryFormatStackCoversScoreChanged
hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126Cursor rulesgobun+5setupbuildtestlint-format+6100/1003 days ago
TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45Cursor rulestypescriptpytest+15testlint-formatstylearch+5100/1003 days ago
dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0Cursor rulestypescriptturborepo+15setuptestlint-formatstyle+799/1003 days ago
deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1Cursor rulestypescriptnextjs+5setuptestlint-formatstyle+799/1003 days ago
markstev/mark-starter.cursor/rules/frontend.mdc · 0Cursor rulestypescriptturborepo+14setuptestlint-formatstyle+699/1003 days ago
Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0Cursor rulestypescriptturborepo+13setuptestlint-formatstyle+799/1003 days ago
langflow-ai/langflow.cursor/rules/docs_development.mdc · 153kCursor rulespythonnode+16setupbuildtestlint-format+797/1003 days ago
TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45Cursor rulestypescriptpytest+15teststyletesting-strategysecurity+397/1003 days ago
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack