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 blocksRepository
0
— · pushed 377 days agoLast changed
3 days ago
First indexed 3 days ago.123456# SoybeanAdmin React TypeScript 规范78## 概述910本文档定义了 SoybeanAdmin React 项目的 TypeScript 使用规范,旨在确保类型安全、提高代码质量和团队协作效率。严格遵循这些规范将帮助我们构建类型安全、可维护的应用程序。1112## 基本原则1314### 🎯 核心原则15161. **类型安全优先**:所有组件和函数必须提供准确的类型定义172. **避免类型逃逸**:禁止使用 `any` 类型,必要时使用 `unknown`183. **明确胜过隐式**:显式声明类型比依赖推断更可靠194. **一致性标准**:使用统一的类型定义和命名约定205. **编译零错误**:确保编译无任何类型错误或警告2122### ⚠️ 严格规则2324```typescript25// ✅ 正确做法26// 1. 禁止使用 any,必要时使用 unknown27function 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}3334// 2. 所有导出类型统一从 src/types 出口引入35import type { UserInfo, ApiResponse } from '@/types';3637// 3. 枚举优先使用 const enum38const enum UserRole {39 Admin = 'admin',40 User = 'user',41 Guest = 'guest'42}4344// 4. 函数参数与返回值必须明确声明类型45function calculateScore(user: UserInfo, factors: ScoreFactor[]): number {46 // 实现逻辑47 return 0;48}4950// 5. 类型别名使用 type,对象结构使用 interface51type Theme = 'light' | 'dark' | 'auto';5253interface UserInfo {54 id: string;55 name: string;56 role: UserRole;57}5859// ❌ 错误做法60function processData(data: any): any { // 禁止使用 any61 return data;62}6364enum UserRole { // 不推荐使用普通 enum65 Admin = 'admin',66 User = 'user'67}6869function calculate(user, factors) { // 缺少类型声明70 return 0;71}72```7374## 组件类型定义7576### 🧩 React 组件规范7778```typescript79// ✅ 正确示例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}100101/**102 * 用户卡片组件状态103 */104interface UserCardState {105 isLoading: boolean;106 error: string | null;107 isExpanded: boolean;108}109110/**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 renderExtra123}) => {124 const [state, setState] = useState<UserCardState>({125 isLoading: false,126 error: null,127 isExpanded: false128 });129130 // 处理编辑操作131 const handleEdit = useCallback((): void => {132 onEdit?.(user);133 }, [user, onEdit]);134135 // 处理删除操作136 const handleDelete = useCallback(async (): Promise<void> => {137 if (!onDelete) return;138139 setState(prev => ({ ...prev, isLoading: true, error: null }));140141 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]);152153 return (154 <div className={clsx('user-card', `user-card--${size}`, className)}>155 {/* 组件实现 */}156 </div>157 );158};159160export default UserCard;161162// 导出组件类型供外部使用163export type { UserCardProps, UserCardState };164```165166167168## 泛型使用规范169170### 🔗 泛型最佳实践171172```typescript173// ✅ 正确示例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}190191/**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}207208/**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}224225/**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}241242/**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}260261// 使用泛型的实际示例262const UserTable: React.FC<TableProps<UserInfo>> = ({ data, columns, loading, onRowClick }) => {263 // 实现逻辑264 return <div>User Table</div>;265};266267// 为复杂泛型提供类型别名268type UserTableProps = TableProps<UserInfo>;269type UserApiResponse = ApiResponse<UserInfo>;270type UserListResponse = ApiResponse<PaginatedResponse<UserInfo>>;271```272273### 🔒 泛型约束274275```typescript276// ✅ 正确示例277/**278 * 确保泛型 T 包含 id 属性279 */280interface HasId {281 id: string;282}283284/**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}291292/**293 * 键值对类型约束294 * @template K 键类型,必须是字符串295 * @template V 值类型296 */297interface KeyValuePair<K extends string, V> {298 key: K;299 value: V;300}301302/**303 * 确保对象类型约束304 * @template T 必须是对象类型305 */306function cloneObject<T extends Record<string, any>>(obj: T): T {307 return { ...obj };308}309310/**311 * React 组件 Props 约束312 * @template P 组件 Props 类型313 */314type ComponentWithProps<P extends Record<string, any>> = React.FC<P>;315```316317## 类型合并与扩展318319### 🔀 交叉类型和联合类型320321```typescript322// ✅ 正确示例323/**324 * 基础用户信息325 */326interface BaseUser {327 id: string;328 name: string;329 email: string;330}331332/**333 * 用户权限信息334 */335interface UserPermissions {336 role: UserRole;337 permissions: string[];338 canEdit: boolean;339 canDelete: boolean;340}341342/**343 * 用户活动信息344 */345interface UserActivity {346 lastLogin: string;347 loginCount: number;348 isActive: boolean;349}350351// 使用交叉类型合并多个接口352type FullUserInfo = BaseUser & UserPermissions & UserActivity;353354// 使用工具类型修改现有类型355type PartialUser = Partial<BaseUser>; // 所有属性可选356type UserNameAndEmail = Pick<BaseUser, 'name' | 'email'>; // 只选择特定属性357type UserWithoutId = Omit<BaseUser, 'id'>; // 排除特定属性358type RequiredUser = Required<Partial<BaseUser>>; // 所有属性必填359360/**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}373374/**375 * 条件类型示例376 */377type ApiResponseType<T> = T extends string378 ? { message: T }379 : T extends number380 ? { code: T }381 : { data: T };382383/**384 * 映射类型示例385 */386type ReadonlyUser = {387 readonly [K in keyof BaseUser]: BaseUser[K];388};389390type OptionalUser = {391 [K in keyof BaseUser]?: BaseUser[K];392};393```394395## 枚举和常量396397### 📝 枚举使用规范398399```typescript400// ✅ 推荐:使用 const enum 和联合类型401/**402 * 用户角色枚举403 */404const enum UserRole {405 Admin = 'admin',406 User = 'user',407 Guest = 'guest',408 SuperAdmin = 'super_admin'409}410411/**412 * 请求状态枚举413 */414const enum RequestStatus {415 Idle = 'idle',416 Loading = 'loading',417 Success = 'success',418 Error = 'error'419}420421// 使用联合类型和 as const422const THEME_MODES = ['light', 'dark', 'auto'] as const;423type ThemeMode = typeof THEME_MODES[number]; // 'light' | 'dark' | 'auto'424425const HTTP_STATUS_CODES = {426 OK: 200,427 NOT_FOUND: 404,428 UNAUTHORIZED: 401,429 FORBIDDEN: 403,430 INTERNAL_ERROR: 500431} as const;432433type HttpStatusCode = typeof HTTP_STATUS_CODES[keyof typeof HTTP_STATUS_CODES];434435/**436 * 复杂常量配置437 */438const APP_CONFIG = {439 api: {440 baseURL: 'https://api.example.com',441 timeout: 10000,442 retryCount: 3443 },444 ui: {445 defaultPageSize: 20,446 maxPageSize: 100,447 themes: THEME_MODES448 },449 features: {450 enableDarkMode: true,451 enableI18n: true,452 enablePWA: false453 }454} as const;455456type AppConfig = typeof APP_CONFIG;457458// 类型守卫函数459/**460 * 检查是否为有效的用户角色461 */462function isValidUserRole(role: string): role is UserRole {463 return Object.values(UserRole).includes(role as UserRole);464}465466/**467 * 检查是否为有效的主题模式468 */469function isValidThemeMode(mode: string): mode is ThemeMode {470 return THEME_MODES.includes(mode as ThemeMode);471}472473// ❌ 不推荐:使用普通 enum474enum BadExample {475 Value1 = 'value1',476 Value2 = 'value2'477}478```479480## 类型推断与断言481482### 🔍 类型守卫和断言483484```typescript485// ✅ 正确示例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}501502/**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}513514/**515 * 使用 typeof 进行类型守卫516 */517function processValue(value: string | number): string {518 if (typeof value === 'string') {519 // TypeScript 知道这里 value 是 string 类型520 return value.toUpperCase();521 }522523 // TypeScript 知道这里 value 是 number 类型524 return value.toString();525}526527/**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}541542/**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;554555 // 进一步验证数组元素556 if (data.every(isUser)) {557 return data; // TypeScript 推断为 UserInfo[]558 }559 }560561 throw new Error('Invalid API response format');562}563564/**565 * 条件类型推断566 */567type ExtractArrayType<T> = T extends (infer U)[] ? U : never;568type StringArrayType = ExtractArrayType<string[]>; // string569type NumberArrayType = ExtractArrayType<number[]>; // number570571/**572 * 工具函数 - 安全获取对象属性573 */574function safeGet<T extends Record<string, any>, K extends keyof T>(575 obj: T,576 key: K577): T[K] | undefined {578 return obj && typeof obj === 'object' ? obj[key] : undefined;579}580581// ❌ 避免的做法582function badExample(data: unknown): UserInfo {583 return data as UserInfo; // 危险的强制断言584}585586function alsobad(user: UserInfo): string {587 return (user as any).someProperty; // 使用 any 绕过类型检查588}589```590591## Hook 类型定义592593### 🪝 自定义 Hook 规范594595```typescript596// ✅ 正确示例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}612613/**614 * 用户信息管理 Hook615 * @param userId 用户ID616 * @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);622623 const fetchUser = useCallback(async (): Promise<void> => {624 if (!userId) return;625626 setLoading(true);627 setError(null);628629 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]);639640 const updateUser = useCallback(async (updates: Partial<UserInfo>): Promise<void> => {641 if (!user) return;642643 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]);652653 useEffect(() => {654 fetchUser();655 }, [fetchUser]);656657 return {658 user,659 loading,660 error,661 refresh: fetchUser,662 updateUser663 };664}665666/**667 * 表格数据管理 Hook668 * @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}678679interface 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}701702function useTable<T extends Record<string, any>>(703 options: UseTableOptions<T>704): UseTableReturn<T> {705 const { fetchData, defaultParams = {}, autoLoad = true } = options;706707 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: 0714 });715 const [params, setParams] = useState(defaultParams);716717 const loadData = useCallback(async (): Promise<void> => {718 setLoading(true);719 setError(null);720721 try {722 const response = await fetchData({723 ...params,724 page: pagination.current,725 pageSize: pagination.pageSize726 });727728 setData(response.list);729 setPagination(prev => ({730 ...prev,731 total: response.total732 }));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]);740741 const handleSetParams = useCallback((newParams: Record<string, any>): void => {742 setParams(newParams);743 setPagination(prev => ({ ...prev, current: 1 }));744 }, []);745746 const reset = useCallback((): void => {747 setParams(defaultParams);748 setPagination(prev => ({ ...prev, current: 1 }));749 }, [defaultParams]);750751 useEffect(() => {752 if (autoLoad) {753 loadData();754 }755 }, [loadData, autoLoad]);756757 return {758 data,759 loading,760 error,761 pagination,762 params,763 setParams: handleSetParams,764 refresh: loadData,765 reset766 };767}768```769770## JSDoc 注释规范771772### 📚 TypeScript JSDoc773774```typescript775// ✅ 正确示例776/**777 * 用户服务类778 * @description 提供用户相关的API操作和数据管理功能779 * @since 1.0.0780 * @author 张三 <zhangsan@example.com>781 */782class UserService {783 private apiClient: ApiClient;784785 /**786 * 构造函数787 * @param apiClient API客户端实例788 */789 constructor(apiClient: ApiClient) {790 this.apiClient = apiClient;791 }792793 /**794 * 获取用户列表795 * @template T 用户数据类型,默认为 UserInfo796 * @param params 查询参数797 * @param params.page 页码,从1开始798 * @param params.pageSize 每页数量,范围1-100799 * @param params.keyword 搜索关键词,支持用户名和邮箱800 * @returns Promise<PaginatedResponse<T>> 分页的用户列表801 * @throws {ApiError} 当请求失败时抛出API错误802 * @example803 * ```typescript804 * 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: UserListParams814 ): Promise<PaginatedResponse<T>> {815 const response = await this.apiClient.get<PaginatedResponse<T>>('/users', {816 params817 });818 return response.data;819 }820821 /**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}837838/**839 * 格式化用户显示名称840 * @param user 用户信息对象841 * @param options 格式化选项842 * @param options.showEmail 是否显示邮箱,默认false843 * @param options.showRole 是否显示角色,默认false844 * @returns 格式化后的显示名称845 * @example846 * ```typescript847 * 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;862863 if (options.showEmail && user.email) {864 displayName += ` (${user.email})`;865 }866867 if (options.showRole && user.role) {868 displayName += ` [${user.role}]`;869 }870871 return displayName;872}873874/**875 * 通用数据转换函数876 * @template TInput 输入数据类型877 * @template TOutput 输出数据类型878 * @param data 输入数据879 * @param transformer 转换函数880 * @returns 转换后的数据881 * @example882 * ```typescript883 * const users = [{ id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }];884 * const userOptions = transformData(users, user => ({885 * label: user.name,886 * value: user.id887 * }));888 * ```889 */890function transformData<TInput, TOutput>(891 data: TInput[],892 transformer: (item: TInput, index: number) => TOutput893): TOutput[] {894 return data.map(transformer);895}896```897898## 类型导出和模块化899900### 📦 类型模块组织901902```typescript903// ✅ src/types/index.ts - 统一类型出口904/**905 * 用户相关类型906 */907export type { UserInfo, UserRole, CreateUserData, UpdateUserData } from './user';908909/**910 * API 相关类型911 */912export type { ApiResponse, ApiError, PaginatedResponse } from './api';913914/**915 * 组件相关类型916 */917export type { TableProps, TableColumn, FormField } from './components';918919/**920 * 应用配置类型921 */922export type { AppConfig, ThemeConfig, RouteConfig } from './config';923924/**925 * 工具类型926 */927export type { DeepPartial, DeepRequired, ValueOf, KeysOfType } from './utils';928929// ✅ src/types/user.ts930/**931 * 用户角色枚举932 */933export const enum UserRole {934 Admin = 'admin',935 User = 'user',936 Guest = 'guest'937}938939/**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}960961/**962 * 创建用户数据963 */964export interface CreateUserData {965 name: string;966 email: string;967 role?: UserRole;968 avatar?: string;969}970971/**972 * 更新用户数据973 */974export type UpdateUserData = Partial<Omit<UserInfo, 'id' | 'createTime' | 'updateTime'>>;975976// ✅ 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};983984/**985 * 深度必需类型986 */987export type DeepRequired<T> = {988 [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];989};990991/**992 * 获取对象值的联合类型993 */994export type ValueOf<T> = T[keyof T];995996/**997 * 获取指定类型的键998 */999export type KeysOfType<T, U> = {1000 [K in keyof T]: T[K] extends U ? K : never;1001}[keyof T];10021003/**1004 * 条件类型 - 如果 T 是 U 的子类型则返回 X,否则返回 Y1005 */1006export type If<T extends U, U, X, Y> = T extends U ? X : Y;10071008/**1009 * 函数参数类型1010 */1011export type FunctionArgs<T> = T extends (...args: infer A) => any ? A : never;10121013/**1014 * 函数返回值类型1015 */1016export type FunctionReturn<T> = T extends (...args: any[]) => infer R ? R : never;1017```101810191020## 最佳实践总结10211022### ✅ 推荐做法102310241. **使用统一的类型导入**1025```typescript1026// ✅ 正确1027import type { UserInfo, ApiResponse } from '@/types';10281029// ❌ 错误1030import { UserInfo } from '../types/user';1031import { ApiResponse } from '../../types/api';1032```103310342. **明确的函数签名**1035```typescript1036// ✅ 正确1037function processUsers(users: UserInfo[]): ProcessedUser[] {1038 return users.map(transformUser);1039}10401041// ❌ 错误1042function processUsers(users: any): any {1043 return users.map(transformUser);1044}1045```104610473. **使用类型守卫而非断言**1048```typescript1049// ✅ 正确1050if (isUserInfo(data)) {1051 console.log(data.name); // TypeScript 知道 data 是 UserInfo1052}10531054// ❌ 错误1055console.log((data as UserInfo).name); // 危险的断言1056```105710584. **导出组件类型**1059```typescript1060// ✅ 正确1061export default UserCard;1062export type { UserCardProps };10631064// ❌ 错误1065export default UserCard;1066// 没有导出 Props 类型1067```10681069### 📋 代码审查检查清单10701071- [ ] 所有函数都有明确的参数和返回值类型1072- [ ] 没有使用 `any` 类型1073- [ ] 所有接口和类型都有 JSDoc 注释1074- [ ] 使用了合适的泛型约束1075- [ ] 导出了所有公共类型1076- [ ] 使用了统一的类型导入路径1077- [ ] 枚举使用了 `const enum`1078- [ ] 复杂类型有类型守卫函数1079- [ ] 编译没有任何错误或警告10801081遵循这些 TypeScript 规范将确保项目具有良好的类型安全性、可维护性和开发体验!1082
Also in crunl/Xingyu-Frontend
Diff this repo’s formatsOne 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?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| crunl/Xingyu-Frontend.cursor/rules/api.mdc · 0 | Cursor rules | api | 45/100 | 3 days ago | |
| crunl/Xingyu-Frontend.cursor/rules/comments.mdc · 0 | Cursor rules | securityapidocs | 46/100 | 3 days ago | |
| crunl/Xingyu-Frontend.cursor/rules/componenting.mdc · 0 | Cursor rules | no sections | 45/100 | 3 days ago | |
| crunl/Xingyu-Frontend.cursor/rules/naming.mdc · 0 | Cursor rules | archtypesapiui | 58/100 | 3 days ago | |
| crunl/Xingyu-Frontend.cursor/rules/project.mdc · 0 | Cursor rules | no sections | 50/100 | 3 days ago | |
| crunl/Xingyu-Frontend.cursor/rules/reduxing.mdc · 0 | Cursor rules | no sections | 45/100 | 3 days ago | |
| crunl/Xingyu-Frontend.cursor/rules/routing.mdc · 0 | Cursor rules | no sections | 53/100 | 3 days ago | |
| crunl/Xingyu-Frontend.cursor/rules/styling.mdc · 0 | Cursor rules | archui | 54/100 | 3 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.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 3 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago |
