Skip to content

类型系统

概述

Ruoyi-Plus-Uniapp 框架建立了完整的 TypeScript 类型体系,通过统一的类型定义规范、严格的类型检查和合理的类型设计模式,为项目提供了强大的类型安全保障。本文档详细介绍框架中的类型系统设计理念、使用规范和最佳实践。

类型系统架构

类型分层结构

📁 类型系统架构
├── 🌐 全局类型 (global.d.ts)        # 项目通用类型定义
├── 🔧 环境类型 (env.d.ts)           # 环境变量类型声明
├── 🛣️ 路由类型 (router.d.ts)        # 路由系统类型扩展
├── 📡 HTTP类型 (http.d.ts)          # 请求响应类型定义
├── 🎨 组件类型 (element.d.ts)       # UI组件类型声明
└── 📦 业务类型 (各模块Types文件)      # 具体业务实体类型

核心类型规范

1. API 类型定义规范

统一响应格式

typescript
// 统一的 API 响应类型
declare type Result<T = any> = Promise<[Error | null, T | null]>

// 标准 API 响应结构
declare interface R<T = any> {
  code: number    // 响应状态码
  msg: string     // 响应消息
  data: T         // 响应数据
}

// 分页响应数据类型
declare interface PageResult<T = any> {
  records: T[]    // 数据记录列表
  total: number   // 总记录数
  pages: number   // 总页数
  current: number // 当前页码
  size: number    // 每页大小
  last: boolean   // 是否为最后一页
}

API 接口类型定义模式

typescript
// 认证相关类型定义 (authTypes.ts)
export interface CaptchaVo {
  tenantEnabled: boolean    // 是否开启多租户
  tenantTitle: string       // 租户标题
  registerEnabled: boolean  // 是否开启注册
  captchaEnabled: boolean   // 是否开启验证码
  uuid: string             // 验证码唯一标识
  img: string              // 验证码图片(Base64编码)
}

export interface AuthTokenVo {
  access_token: string      // 访问令牌
  refresh_token?: string    // 刷新令牌
  expire_in: number         // 访问令牌有效期(秒)
  refresh_expire_in?: number // 刷新令牌有效期(秒)
  scope?: string           // 令牌权限范围
}

// 联合类型用于多种登录方式
export type LoginRequest = PasswordLoginBody | EmailLoginBody | SmsLoginBody | SocialLoginBody

用户相关类型定义模式

typescript
// 用户查询参数类型
export interface SysUserQuery extends PageQuery {
  userName?: string         // 用户账号
  phone?: string           // 手机号码
  status?: string          // 帐号状态
  deptId?: string | number // 部门id
  roleId?: string | number // 角色id
}

// 用户表单类型
export interface SysUserBo {
  userId?: string | number  // 用户ID
  userName: string         // 用户账号(必填)
  nickName?: string        // 用户昵称
  password: string         // 密码(必填)
  phone?: string          // 手机号码
  email?: string          // 用户邮箱
  status: string          // 帐号状态(必填)
  postIds: string[]       // 岗位id列表(必填)
  roleIds: string[]       // 角色id列表(必填)
}

// 用户视图对象类型
export interface SysUserVo {
  userId: string | number  // 用户ID
  tenantId: string        // 租户id
  userName: string        // 用户账号
  nickName: string        // 用户昵称
  email: string           // 用户邮箱
  phone: string          // 手机号码
  avatar: string         // 头像地址
  status: string         // 帐号状态
  roles: SysRoleVo[]     // 角色列表
  admin: boolean         // 是否管理员
  createTime?: string    // 创建时间
}

2. 组件 Props 类型规范

表单组件类型定义模式

typescript
// AFormInput 组件 Props 接口定义
interface AFormInputProps {
  // 基础属性 - 明确类型,支持多种数据类型
  modelValue?: string | number | null | undefined
  label?: string
  placeholder?: string
  
  // 布局属性 - 支持灵活的尺寸设置
  width?: number | string
  labelWidth?: number | string
  span?: number
  
  // 功能属性 - 明确的布尔类型和枚举类型
  showFormItem?: boolean
  showPassword?: boolean
  clearable?: boolean
  disabled?: boolean
  
  // 枚举类型 - 限制可选值
  type?: 'text' | 'textarea' | 'number' | 'password'
  size?: '' | 'default' | 'small' | 'large'
  
  // 复杂类型 - 对象类型的明确定义
  autosize?: { minRows?: number; maxRows?: number }
  
  // 数字输入框特有属性 - 条件类型
  min?: number
  max?: number
  step?: number
  precision?: number
}

// 使用 withDefaults 提供默认值
const props = withDefaults(defineProps<AFormInputProps>(), {
  modelValue: undefined,
  label: '',
  type: 'text',
  clearable: true,
  showFormItem: true,
  autosize: () => ({ minRows: 2, maxRows: 30 })
})

3. 状态管理类型规范

typescript
// 用户状态管理类型定义
export const useUserStore = defineStore('user', () => {
  // 明确的状态类型定义
  const token = ref<string>(tokenUtils.getToken())
  const userInfo = ref<SysUserVo | null>(null)
  const roles = ref<string[]>([])
  const permissions = ref<string[]>([])

  // 方法的返回类型明确定义
  const loginUser = async (loginRequest: LoginRequest): Result<void> => {
    const [err, data] = await userLogin(loginRequest)
    if (err) {
      return [err, null]
    }
    // 类型安全的操作
    tokenUtils.setToken(data.access_token, data.expire_in)
    token.value = data.access_token
    return [null, null]
  }

  const fetchUserInfo = async (): Result<void> => {
    // 类型安全的API调用
    const [err, data] = await getUserInfo()
    if (err) return [err, null]
    
    // 明确的类型操作
    userInfo.value = data.user
    roles.value = data.roles || []
    permissions.value = data.permissions || []
    
    return [null, null]
  }

  return {
    // 状态
    token,
    userInfo,
    roles,
    permissions,
    // 方法
    loginUser,
    fetchUserInfo
  }
})

类型使用模式

1. 泛型的使用场景

API 响应泛型

typescript
// 泛型API函数定义
export const pageUsers = (query: SysUserQuery): Result<PageResult<SysUserVo>> => {
  return http.get<PageResult<SysUserVo>>('/system/user/pageUsers', query)
}

export const getUser = (userId?: string | number): Result<SysUserInfoVo> => {
  return http.get<SysUserInfoVo>(`/system/user/getUser/${parseStrEmpty(userId)}`)
}

// 泛型工具类型
type Optional<T> = {
  [K in keyof T]?: T[K]
}

type RequiredKeys<T, K extends keyof T> = T & Required<Pick<T, K>>

组件泛型

typescript
// 表格组件泛型定义
interface TableProps<T = any> {
  data: T[]
  columns: TableColumn<T>[]
  loading?: boolean
}

interface TableColumn<T> {
  prop: keyof T
  label: string
  width?: number
  formatter?: (row: T, column: TableColumn<T>, cellValue: any) => string
}

2. 联合类型和交叉类型

typescript
// 联合类型 - 多种可能的类型
type LoginType = 'password' | 'email' | 'sms' | 'social'
type Status = '0' | '1'  // 启用禁用状态
type ElTagType = 'primary' | 'success' | 'info' | 'warning' | 'danger'

// 交叉类型 - 合并多个类型
interface BaseEntity {
  createTime?: string
  updateTime?: string
  createBy?: string
  updateBy?: string
}

interface UserEntity extends BaseEntity {
  userId: string | number
  userName: string
  nickName: string
}

// 条件类型组合
type LoginBody = {
  authType: LoginType
  code?: string
  uuid?: string
}

type PasswordLogin = LoginBody & {
  authType: 'password'
  userName: string
  password: string
  rememberMe?: boolean
}

3. 类型守卫和断言

typescript
// 类型守卫函数
function isPasswordLogin(body: LoginRequest): body is PasswordLoginBody {
  return body.authType === 'password'
}

function isValidUser(user: any): user is SysUserVo {
  return user && typeof user.userId !== 'undefined' && typeof user.userName === 'string'
}

// 使用类型守卫
function processLogin(loginData: LoginRequest) {
  if (isPasswordLogin(loginData)) {
    // 这里 TypeScript 知道 loginData 是 PasswordLoginBody 类型
    console.log(loginData.userName) // 类型安全
  }
}

// 类型断言
const userInfo = data as SysUserVo
const formRef = ref() as Ref<ElFormInstance>

4. 条件类型的应用

typescript
// 根据条件返回不同类型
type ApiResponse<T> = T extends void 
  ? { code: number; msg: string } 
  : { code: number; msg: string; data: T }

// 提取属性类型
type UserKeys = keyof SysUserVo  // 'userId' | 'userName' | 'nickName' | ...

// 构造新类型
type PartialUser = Partial<SysUserVo>  // 所有属性可选
type RequiredUser = Required<Pick<SysUserVo, 'userId' | 'userName'>>  // 指定属性必填

业务类型设计

查询参数类型模式

typescript
// 基础查询参数
interface BaseQuery {
  pageNum?: number
  pageSize?: number
  orderByColumn?: string
  isAsc?: string
  searchValue?: string
}

// 具体业务查询参数继承基础查询
interface AdQuery extends BaseQuery {
  id?: string | number
  appid?: string
  adName?: string
  adType?: string
  status?: string
}

表单对象类型模式

typescript
// Bo (Business Object) - 业务对象
interface AdBo {
  id?: string | number     // 可选 - 新增时无ID
  appid?: string          // 可选属性
  adName?: string         // 可选属性
  adType?: string         // 可选属性
  status: string          // 必填属性
  sortOrder: number       // 必填属性
}

// Vo (View Object) - 视图对象
interface AdVo {
  id: string | number     // 必填 - 视图对象总是有ID
  appid: string
  adName: string
  adType: string
  status: string
  sortOrder: number
  createTime?: string     // 可选 - 时间字段
  updateTime?: string
}

类型声明扩展

Element Plus 类型扩展

typescript
// 全局组件实例类型
declare global {
  // 表单相关
  declare type ElFormInstance = ep.FormInstance
  declare type ElFormRules = ep.FormRules
  
  // 表格相关
  declare type ElTableInstance = ep.TableInstance
  declare type ElTableColumnCtx<T> = ep.TableColumnCtx<T>
  
  // 上传相关
  declare type ElUploadInstance = ep.UploadInstance
  declare type ElUploadFile = ep.UploadFile
  
  // 样式类型
  declare type ElTagType = 'primary' | 'success' | 'info' | 'warning' | 'danger'
  declare type ElSize = 'large' | 'default' | 'small'
}

路由类型扩展

typescript
// 扩展 Vue Router 类型
declare module 'vue-router' {
  interface RouteMeta {
    title?: string          // 路由标题
    icon?: string          // 路由图标
    hidden?: boolean       // 是否隐藏
    permissions?: string[] // 访问权限
    roles?: string[]       // 访问角色
  }
  
  interface TagView {
    fullPath?: string
    name?: string
    path?: string
    title?: string
    meta?: RouteMeta
  }
}

最佳实践

1. 命名约定

typescript
// 接口命名 - 使用 I 前缀或描述性后缀
interface UserInfo { }          // ✅ 推荐
interface IUserService { }      // ✅ 可选
interface SysUserVo { }         // ✅ 业务对象

// 类型别名命名 - 使用 Type 后缀或描述性名称
type LoginType = 'password' | 'email'    // ✅ 推荐
type UserStatus = '0' | '1'              // ✅ 推荐
type ApiResponseType<T> = Promise<T>     // ✅ 可选

// 泛型参数命名
interface ApiResponse<TData> { }         // ✅ 描述性
interface TableColumn<T, K extends keyof T> { } // ✅ 约束明确

2. 类型文件组织

typescript
// 按模块组织类型文件
src/api/
├── system/
│   ├── auth/
│   │   ├── authApi.ts      # API 函数
│   │   └── authTypes.ts    # 类型定义
│   └── user/
│       ├── userApi.ts
│       └── userTypes.ts
└── business/
    └── ad/
        ├── adApi.ts
        └── adTypes.ts

// 类型导出规范
// authTypes.ts
export interface CaptchaVo { }
export interface LoginRequest { }
export interface AuthTokenVo { }

// authApi.ts
import type { CaptchaVo, LoginRequest, AuthTokenVo } from './authTypes'

3. 类型复用策略

typescript
// 基础类型定义
interface BaseEntity {
  createTime?: string
  updateTime?: string
  createBy?: string
  updateBy?: string
}

// 继承基础类型
interface SysUserVo extends BaseEntity {
  userId: string | number
  userName: string
  // ... 其他属性
}

// 工具类型复用
type PickRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>
type UserFormData = PickRequired<SysUserVo, 'userName' | 'status'>

4. 类型安全检查

typescript
// 运行时类型检查
function validateUser(user: any): user is SysUserVo {
  return (
    user &&
    typeof user.userId === 'string' &&
    typeof user.userName === 'string' &&
    typeof user.status === 'string'
  )
}

// 使用类型检查
function processUser(userData: unknown) {
  if (validateUser(userData)) {
    // 类型安全的操作
    console.log(userData.userName)
  }
}

5. 错误处理类型化

typescript
// 统一的错误处理类型
type ApiError = {
  code: number
  message: string
  details?: any
}

// Result 类型模式
type Result<T, E = ApiError> = Promise<[E | null, T | null]>

// 使用示例
const [error, data] = await getUserInfo()
if (error) {
  // 错误处理
  console.error(error.message)
  return
}
// 成功处理 - data 类型安全
console.log(data.userName)

性能优化建议

1. 类型推断优化

typescript
// ❌ 避免过度指定类型
const user: SysUserVo = {
  userId: '1' as string | number,  // 不必要的断言
  userName: 'admin' as string      // 不必要的断言
}

// ✅ 利用类型推断
const user: SysUserVo = {
  userId: '1',      // TypeScript 自动推断
  userName: 'admin' // TypeScript 自动推断
}

2. 条件类型优化

typescript
// ❌ 复杂的条件类型
type ComplexType<T> = T extends string 
  ? T extends `${string}@${string}` 
    ? EmailType<T> 
    : StringType<T>
  : T extends number 
    ? NumberType<T> 
    : DefaultType<T>

// ✅ 简化的类型定义
type EmailString = `${string}@${string}`
type ProcessedType<T> = T extends EmailString ? EmailType<T> : BasicType<T>

总结

Ruoyi-Plus-Uniapp 的类型系统通过系统化的设计和规范,实现了:

  • 类型安全: 编译时错误检查,减少运行时错误
  • 开发效率: 智能提示和自动补全,提升开发体验
  • 代码质量: 统一的类型规范,提高代码可读性和维护性
  • 扩展性: 灵活的类型扩展机制,适应业务发展需求

通过遵循本文档中的类型设计模式和最佳实践,可以构建出类型安全、高效且易于维护的 TypeScript 应用程序。