TypeScript 在 uni-app 中是中大型项目可维护性的底线,必须启用严格模式、正确配置 tsconfig.json 并统一接口/props/响应类型,否则类型约束形同虚设。

直接上结论:TypeScript 在 uni-app 里不是“可选加分项”,而是中大型项目维持可维护性的底线。不加类型约束的 uni.request、props、store 状态,写得越快,后期改得越疼。
uni-app 项目必须启用 TypeScript 编译支持
很多团队卡在这一步就停了——以为装了 typescript 和 @dcloudio/types 就算接入成功。其实关键在 tsconfig.json 的实际生效配置:
- 确保
"strict": true已开启,否则any会泛滥成灾,失去类型检查意义 -
"types"字段必须包含"@dcloudio/types"(uni-app 官方类型)和"miniprogram-api-typings"(小程序 API 类型),缺一不可 -
"include"要覆盖src/**/*.{ts,tsx,mts,cts,vue},否则.vue文件里的<script lang="ts"></script>不会被校验 - 如果用 HBuilderX,还需确认「构建时启用 TypeScript 编译」选项已勾选(菜单:运行 → 运行到小程序模拟器 → 设置)
接口响应类型必须用泛型统一收口
别再让每个 uni.request 的 res.data 是 any。后端字段名拼错一个字母,运行时才报 Cannot read property 'list' of undefined,这种错误本该在编辑器里就标红。
推荐在 src/typings/api.uts(或 .ts)中定义:
export interface ApiResponse<T = any> {
code: number
message: string
data: T
timestamp?: number
}
调用时显式传入泛型:
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
const res = await uni.request<ApiResponse<UserInfo[]>>({
url: '/api/user/list',
method: 'GET'
})
// 此时 res.data 自动推导为 UserInfo[] 类型,点 . 就有提示
- 避免直接用
any或unknown当返回类型占位,那等于没加类型 - 分页接口建议额外封装
PageResponse<t></t>,把pageNum/pageSize/total提前收敛 - 如果后端返回结构不统一(比如有的带
data,有的直接是数组),宁可写个适配函数做 normalize,也不要妥协类型
组件 props 必须用 interface + defineProps 声明
Vue 3 的 defineProps 配合 TypeScript 接口,是防止 props 传参错漏最有效的手段。别再用字符串数组或对象形式声明:
// ❌ 错误:无类型、无默认值约束、无必填校验
defineProps(['title', 'content'])
// ✅ 正确:明确类型、可选/必填、IDE 实时提示
interface Props {
title: string
content: string
isPublished?: boolean
onConfirm?: () => void
}
const props = defineProps<Props>()
- 函数类型 props(如回调)必须显式写出签名,否则
props.onConfirm()会丢失类型检查 - 避免在
setup里用ref或reactive包裹 props 值再暴露,这会切断响应性且掩盖原始类型 - 复杂嵌套结构建议抽离为独立
interface,放在src/typings/components/下统一管理
请求封装里最容易被忽略的类型断言陷阱
很多人封装 request 时,习惯在 success 回调里对 res.data 做类型断言,比如 res.data as User。这看似解决了类型问题,实则埋下隐患:
- 断言不会校验数据真实性,后端字段缺失或类型不符时照样通过编译
- 无法享受泛型自动推导,每次都要手动写
as,违背 TypeScript 设计初衷 - 真正健壮的做法是:用
zod或io-ts做运行时校验,或至少用asserts函数兜底
更轻量的替代方案是,在封装函数签名中强制泛型透传:
export async function request<T>(options: UniApp.RequestOptions): Promise<T> {
const res = await uni.request(options)
if (res[1]?.code !== 200) throw new Error(res[1]?.message || '请求失败')
return res[1]?.data as T // 这里只是过渡,最终应替换为 schema 校验
}
注意:res[1] 是 uni.request Promise 返回的第二个参数(即 success 回调结果),这是 uni-app x 的约定;旧版需用回调风格,但类型穿透更麻烦,建议升级。
最关键的不是语法多漂亮,而是让每个接口调用点都能看到「我传了什么、会拿到什么、错在哪」——这三点没落实,TypeScript 就只是披了层皮。

















