Files
medical-mall/pages/mall/consumer/doc/uts.txt

1397 lines
52 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
================================================================================
UTS-Android 兼容性开发规范
================================================================================
> 以下为 uni-app-x (UTS) Android 端开发常见注意事项与踩坑点,建议所有开发成员遵循:
================================================================================
一、基础语法规范
================================================================================
1. 变量声明
- 只能使用 let 和 const不能使用 var
- 变量声明必须有显式类型或初始化值
- 不支持 undefined 类型,变量未赋值就是 null
- 不支持 undefined 关键字,判断是否存在要用 != null
2. 类型定义
- 只适合转 type不适合使用 interfaceinterface 在 kotlin/swift 中另有不同)
- 不支持 Intersection Type交叉类型
- 不支持 Index Signature索引签名
- 类型推断严格,必要时用 as Type 明确类型
- 不支持内联对象类型Object Literal Type需要单独定义 type
3. 函数定义
- 函数必须在使用前定义(不支持函数提升)
- 在 setup 模式下,调用的函数必须在调用之前定义
- 依赖关系需要明确:被调用的函数必须先定义
- 这与 JavaScript 的函数提升行为不同UTS 更接近 C/Java 的编译方式
4. 循环
- for 循环的 i 必须写明类型let i: Int = 0
- 不要用 forEach、map数组遍历用 for 循环
- 嵌套的数组方法调用可能导致类型推断失败,应改用 for 循环
================================================================================
二、类型与对象访问
================================================================================
1. any 类型访问
- 不能直接访问 any 类型对象的属性
- 需要将对象转换为 UTSJSONObject 类型后使用 getString()、getNumber() 等方法访问属性
- any 类型属性访问需转换为 Record<string, any> 后用索引访问
- 使用索引访问属性时,推荐使用方括号语法 obj['property'] 而非点语法 obj.property
- any 类型不支持索引访问 obj['key'],必须先转换为 UTSJSONObject
2. UTSJSONObject 使用
- 用 utils/utis 下的 UTSJSONObject 做类型转换
- 不要用 safeget只要 UTSJSONObject 就好了
- 需要创建动态对象时,应使用 new UTSJSONObject() 然后调用 .set() 方法
- 对于 type 定义的对象类型,同样需要使用 UTSJSONObject
- 使用 getString()、getNumber() 方法获取属性值
3. 数组类型
- 数组类型建议写成 Array<Type>,不要用 Type[] 简写
- 空数组需要明确指定类型,如 [] as string[]
- 数组元素需要明确的类型定义才能在模板中正确访问属性
- 对于 any[] 或 reactive 数组,访问元素属性时需要先转换为 Record<string, any> 或 any[]
4. 对象操作
- 不支持 Object.keys()、Object.values()、Object.entries()
- 不支持 Record<K, V> 对象字面量语法
- 对象字面量 {...} 只能用于构造类型class不能用于接口interface
- reactive 对象在 UTS 中不支持索引器赋值操作
================================================================================
三、条件判断与逻辑运算
================================================================================
1. if 条件
- if 判断只接受 boolean 类型,不能是其他类型的值
- 判断空要用 !== null不能用 !变量uts android 不支持 !在变量前面的判断空方式)
- 模板中的 || 运算符左边必须是 boolean 类型
- 可空类型使用可选链 ?. 和空值合并 ??
- 字符串判断空要用variable != null && variable !== ''
2. 逻辑运算符
- || 表示逻辑或
- && 表示逻辑与
- ! 表示逻辑非(但 !变量 不支持用于判断空)
- ?? 表示空值合并运算符(当左侧为 null 时返回右侧值)
- ts 的为空则使用默认值的语法在 uts 中不能用 ||,要用 ?? 来代替
================================================================================
四、组件与模板
================================================================================
1. 表单与输入
- 表单优先用 form 组件
- 不支持 uni-easyinput用 input 代替
- 时间选择用 uni_modules/lime-date-time-picker
2. 选择器
- uts android 不支持 picker用 picker-view 或 uni.showActionSheet
- 一维的优先用 uni.showActionSheet
- picker-view 的事件用 UniPickerViewChangeEvent
3. 导航与布局
- 不支持 uni-nav-bar先删除
- 不支持 uni-data-select用 picker-view 代替
- 不支持 uni-datetime-picker用 components/picker-date 或 components/picker-time 代替
- 不支持 uni-icons
4. 模板注意事项
- 跟 template 交互的变量尽量用一维变量(不要嵌套对象)
- 模板中可空类型必须使用 ?. 安全访问
- 模板中访问可空类型属性前必须先判空 v-if="order != null"
================================================================================
五、CSS 样式限制
================================================================================
1. 布局方式
- 只支持 display: flex
- 不支持 display: grid
- 不支持 gap
- 不支持 table、grid、grid-template-columns
2. 单位与计算
- 不支持 calc()
- 不支持的单位: vh
- property value `100%` is not supported for min-height (supported values are: number|pixel)
- property value `calc(33.33% - 10px)` is not supported for min-width
3. 选择器
- [APP-ANDROID] 不支持伪类选择器
- [APP-IOS] 不支持伪类选择器
- ERROR: Selector `.login-button[disabled]` is not supported. uvue only support classname selector
4. 其他样式
- WARNING: `backdrop-filter` is not a standard property name
- style property `white-space` is only supported on `<text>|<button>`
- ERROR: property value `all` is not supported for `transition-property`
================================================================================
六、scroll-view 使用
================================================================================
- scroll-view 在 uni-app-x 中不是用 scroll-y=true
- 而是要用 direction="vertical"
================================================================================
七、异步与回调
================================================================================
- uni.showModal 的 success 回调不能是 async 函数
- 解决方案:创建独立的 async 函数,在回调中调用
- UTS 中箭头函数 () => {} 有时会导致"Parenthesized expression cannot be empty"错误
- 解决方案:使用普通函数 function name(): Type {} 代替箭头函数
================================================================================
八、响应式数据
================================================================================
- 对于需要整体替换的数组,推荐使用 ref 而非 reactive
- 使用 ref 时,通过 .value 进行整体替换可以正确触发响应式更新
- ref 数组元素不能直接整体替换,需要修改元素属性
- 对于可能为 null 的参数,需要显式检查后再传递给函数
================================================================================
九、类型导入
================================================================================
- 类型导入需要使用 type 关键字
- 一般情况下,尽可能用强类型模式
- uni_modules 的情况下,尽量把 type 定义到 interface 里面
- 数据获取争取都用强类型方式,查询或 rpc 查询用 supa.from.executeAs<T>() 方式
- 返回的是 resultresultdata 一般可以 as Array<T>
================================================================================
十、常见错误速查
================================================================================
1. "Unresolved reference" - 函数未定义或顺序错误
2. "Cannot create an instance of an abstract class" - Record<K,V> 不能实例化
3. "Assignment type mismatch" - 类型不匹配,需要显式类型转换
4. "if condition must be boolean" - if 条件必须是 boolean 类型
5. "Index Signature is not supported" - 不支持索引签名
6. "Intersection Type is not supported" - 不支持交叉类型
7. "Parenthesized expression cannot be empty" - 箭头函数问题,改用普通函数
8. "找不到名称" - 属性访问问题,需转换为 UTSJSONObject
9. "参数类型不匹配" - 参数类型错误,需要显式类型转换
================================================================================
十一、简明速记30条
================================================================================
1. 表单优先用 form 组件
2. 跟 template 交互的变量尽量用一维变量
3. 不要用 forEach、map、safeget数组遍历用 for 循环,类型转换用 UTSJSONObject
4. 数组类型建议写成 Array<Type>,不要用 Type[] 简写
5. 不支持 undefined变量未赋值就是 null判断用 != null
6. 变量声明只能用 let 或 const不能用 var
7. 判断空要用 !== null不能用 !变量
8. 只支持 type不建议用 interface
9. for 循环的 i 必须写明类型let i: Int = 0
10. 逻辑或用 ||,空值合并用 ??,不能混用
11. if 判断只能是 boolean 类型
12. 不支持索引签名Index Signature
13. 类型推断严格,必要时用 as Type 明确类型
14. 不支持 Intersection Type
15. picker 用 picker-view 或 uni.showActionSheet 替代
16. 样式只支持 display: flex不支持 gap、grid、calc()、伪类选择器
17. scroll-view 用 direction="vertical"
18. 不支持 table、grid、vh 单位、min-width: 100% 等
19. 组件事件如 picker-view 用 UniPickerViewChangeEvent
20. 时间选择用 uni_modules/lime-date-time-picker
21. 类型转换建议用 utils/utis 下的 UTSJSONObject
22. 在 uts setup 的 android 模式下,调用的函数必须在调用之前定义
23. 箭头函数不支持默认参数值,改用显式传参或普通函数定义
24. 不支持 Number()、String() 构造函数,用 as 类型转换
25. 不支持 Object.keys(),用 JSON.stringify() 或 for 循环
26. 不支持 typeof xxx === 'function',用 try-catch 替代
27. 不支持 as unknown as 语法
28. parseInt() 参数必须是 string 类型
29. decodeURIComponent() 返回可空类型,需要处理 null
30. charCodeAt() 返回可空类型,需要处理 null
31. 不支持内联对象类型,需要在 types 文件中单独定义
32. UTSJSONObject.get() 返回可空类型 Any?,需要处理 null 并转换为具体类型
33. Array<any> 元素不能直接访问属性,需转换为 UTSJSONObject 或定义明确类型
34. 类型定义中属性可能为 null 时,必须声明为可空类型(如 any | null
35. switch 语句在某些版本可能有问题,建议用 if-else 替代
36. 模板中可选链 ?.length 需要改为显式判断v-if="arr != null && arr.length > 0"
37. 解构赋值 const { data, error } 在 UTS 中可能有问题,建议用 response.data 方式访问
38. response.data 返回 Any?,赋值前需要判断 null 并类型转换
39. 空数组 [] 无法推断类型需要显式声明let arr: Array<any> = [] 或先判断 null 再转换
40. ref 对象字面量需要定义类型const obj = ref<MyType>({...} as MyType),否则属性访问会报错
41. if 条件必须是 boolean可空类型要用 != null 判断if (obj != null) 而非 if (obj)
42. throw 语句不能抛出 Any 类型,需要处理错误而非抛出
43. 模板中可选链 ?.property 需要改为三元表达式obj != null ? obj.property : ''
44. ref<any> 在模板中无法访问属性必须定义明确类型ref<MyType | null>(null)
45. 展开运算符 [...arr] 不支持,需要手动复制数组
46. Array.from(new Set()) 不支持,需要手动去重
47. Promise.all 可能有问题,建议改为顺序执行
48. .sort(() => Math.random() - 0.5) 随机排序不支持,需要手动实现
49. 数组索引访问 arr[index] 可能越界,建议用 if-else 替代数组查找
50. 事件对象 e.detail.value 需要转换为 UTSJSONObject 后访问
51. String() 构造函数不支持,用 as string 类型转换
52. 数组类型简写 string[] 需要改为 Array<string>
53. any 类型参数不能直接访问属性,需要转换为 UTSJSONObject 后使用 get/getString/getNumber 方法
54. 模板中 !变量 取反不支持改为显式判断v-if="str == ''" 或 v-if="bool == false"
55. 模板中 :class="{ 'class': condition }" 对象语法可能有问题,改为三元表达式::class="condition ? 'class' : ''"
56. supabase .update() 参数需要 UTSJSONObject 类型,用 new UTSJSONObject() 创建并用 .set() 设置属性
57. ref<Array<any>> 在模板中无法访问元素属性,必须定义明确的类型后才能访问
58. 函数参数可以用联合类型func(item: TypeA | TypeB)
59. JSON.stringify(UTSJSONObject) 可能有问题,需要手动拼接字符串
60. UTSJSONObject.keys() 方法不存在,无法获取键列表
61. 联合类型参数不能直接访问属性需要先类型转换const id = (item as TypeA).id
62. 某些 uni API 可能不存在(如 navigateToMiniProgram需要检查或替换
63. JSON.parse(JSON.stringify(obj)) 复杂转换可能有问题,简化处理
64. showModal success 回调不能是 async 函数,需要改为同步或使用 Promise
65. supa.auth.signOut() 等 supabase auth 方法可能不支持,需要简化处理
66. 模板中可空字符串判断 userInfo.phone ? 改为 userInfo.phone != null && userInfo.phone != ''
67. Promise.all() 可能有问题,建议改为顺序执行或 setTimeout
68. .then() 回调可能有问题,建议用 async/await 或直接调用
69. let res: any = null 不支持,改为 let res: any = {} 或其他默认值
70. 类型定义中没有的字段不能赋值,检查类型定义后移除多余字段
71. ref<Array<any>> 在模板中无法访问元素属性,必须定义明确的类型
72. 模板中复杂表达式如 parseFloat(String(x)) 不支持,简化为直接比较
73. forEach 不支持,改用 for 循环
74. any 类型数组元素不能直接访问属性,需转换为 UTSJSONObject
75. showModal success 回调不能是 async 函数,需要改为同步调用独立 async 函数
76. 被生命周期钩子调用的函数必须在钩子之前定义,包括 onMounted、watch、onUnmounted 等
77. 箭头函数不支持默认参数值,改用显式传参或普通函数定义
78. 对象字面量赋值给 ref<Type> 需要显式类型声明const obj: Type = {...} as Type
79. 模板中访问对象属性时,类型定义必须包含该属性,否则报"找不到名称"错误
80. !variable 取反操作不支持,改为 variable == '' 或 variable == false
81. supa.auth 方法不支持,需要简化处理或移除
82. setInterval 回调中使用外部变量需要先声明let timer: number = 0然后在回调中赋值
83. $t() 国际化函数在模板中可能有问题,建议使用硬编码文本或自定义翻译函数
84. profile.username ?? $t('xxx') 混合表达式不支持改为条件判断profile != null && profile.username != null ? profile.username : '默认值'
85. 可选链操作符 ?. 在某些场景不支持,如 currentPage?.options需要改为 if 判断
86. as any[] 类型转换后无法访问属性,需要使用正确的类型如 UTSJSONObject
87. 可空类型 string | null 传给需要 string 的函数需要显式类型转换redirect as string
88. setInterval 回调中修改外部变量,需要用 ref 而不是 let 声明变量,避免 smart cast 问题
89. 非空断言操作符 ! 在某些场景仍然无法解决类型问题,建议简化逻辑避免复杂类型转换
90. decodeURIComponent 函数参数类型严格,可空类型即使使用 ! 也可能报错,建议简化或避免使用
91. getCurrentPages() 获取页面 options 复杂且容易出错,建议简化跳转逻辑
92. 模板中内联箭头函数不支持类型注解,如 @input="(e: any) => ..." 会报错,改用 v-model
93. :class="{ disabled: codeDisabled }" 对象语法可能有问题,改为三元表达式 :class="codeDisabled ? 'disabled' : ''"
94. 使用外部类型定义时,确保所有属性都有默认值,避免 null 导致类型不匹配
95. Supabase insert/update 在 .uvue 文件中直接调用可能报类型错误,建议封装到 .uts 服务文件中调用
96. 可空类型属性在模板中使用时需要处理 nullprofile.gender ?? 'other'
97. 可空数字类型比较前需要先检查 nullprofile.height_cm != null && profile.height_cm > 0
98. CSS 伪类选择器 :last-child、:first-child、:nth-child() 不支持,需要移除
99. Supabase 批量 insert 数组参数类型不匹配,需要改为循环逐条 insert
100. CSS 中孤立的属性没有选择器会导致编译错误Return type mismatch: expected 'Map<String, Map<String, Map<String, Any>>>'
101. response.data as Type[] 直接类型转换会报 ClassCastException需要手动遍历数组并逐个转换类型
102. UTS110111163: 对象字面量只能赋值给 type 定义的类型,不能赋值给 interface 定义的类型,
需要将 interface 改为 type
103. Supabase 返回的数据已经是 UTSJSONObject 类型,可以直接 as UTSJSONObject 使用,不需要 instanceof 检查
104. UTSJSONObject.getString() 在字段类型不是 String 时会报 ClassCastException应使用 get() 方法获取原始值后手动类型检查:
const val = obj.get('field')
const str = (typeof val == 'string') ? (val as string) : ''
105. instanceof String 不支持,应使用 typeof val == 'string' 进行类型检查
================================================================================
十二、整理时间2026-02-25 新增
================================================================================
1. 构造函数限制
- 不支持 Number() 构造函数,使用 as number 类型转换
- 不支持 String() 构造函数,使用 as string 类型转换
- 示例Number(x) → x as numberString(i) → i as string
2. 取反操作符限制
- 不支持 !变量 的取反操作符用于判断空
- 字符串判断空variable == null || variable === ''
- 示例:!this.selectedSkuId → (this.selectedSkuId == null || this.selectedSkuId === '')
3. parseInt/parseFloat 限制
- parseInt() 参数必须是 string 类型
- 如果变量是 number 类型,直接使用,不要调用 parseInt
- 示例parseInt(this.quantity) 错误quantity 是 number直接用 this.quantity
4. Object.keys() 不支持
- 不支持 Object.keys() 方法
- 替代方案:使用 JSON.stringify() 或 for 循环遍历
- UTSJSONObject 可用 .keys() 方法(但某些版本可能不支持)
5. typeof 函数检查不支持
- 不支持 typeof xxx === 'function' 语法
- 替代方案:使用 try-catch 包裹方法调用
6. as unknown as 语法不支持
- 不支持 as unknown as 双重类型转换
- 直接使用 as 目标类型obj as UTSJSONObject
7. 可空类型方法返回值
- decodeURIComponent() 返回 String?,需要处理 null
- charCodeAt() 返回 Number?,需要处理 null
- 示例const code = str.charCodeAt(i); if (code != null) { ... }
8. 内联对象类型不支持
- 不支持 Array<{id: string, name: string}> 这种内联类型定义
- 需要在 types 文件中单独定义 type
- 示例:定义 type ItemType = { id: string, name: string },然后使用 Array<ItemType>
9. eventChannel 不支持
- uni.navigateTo 的 success 回调中 res.eventChannel 不支持
- 替代方案:使用 Storage 或全局变量传递数据
10. 链式调用问题
- .map().join() 链式调用可能导致类型推断失败
- 替代方案:使用 for 循环或分步处理
11. v-model 类型限制
- input 的 v-model 期望 string 类型
- 如果变量是 number使用 :value="variable.toString()" 替代 v-model
================================================================================
十三、setup 模式函数定义顺序(重要)
================================================================================
在 <script setup lang="uts"> 中,函数定义顺序至关重要:
1. 基本规则
- 函数必须在调用之前定义(不支持 JavaScript 的函数提升)
- 这与 JavaScript 的行为不同UTS 更接近 C/Java 的编译方式
2. watch 和 onMounted 中的函数调用
- watch() 和 onMounted() 是立即执行的
- 它们内部调用的函数必须在 watch/onMounted 之前定义
- 错误示例:
```typescript
// 错误resetData 和 loadRefunds 还没定义
watch(activeTab, () => {
resetData()
loadRefunds()
})
const resetData = () => { ... }
const loadRefunds = async () => { ... }
```
- 正确示例:
```typescript
// 正确!先定义函数
const resetData = () => { ... }
const loadRefunds = async () => { ... }
// 再调用 watch/onMounted
watch(activeTab, () => {
resetData()
loadRefunds()
})
onMounted(() => {
loadRefunds()
})
```
3. 推荐的代码组织顺序
```typescript
<script setup lang="uts">
// 1. 导入语句
import { ref, watch, onMounted } from 'vue'
import { supabaseService } from '@/utils/supabaseService'
// 2. 类型定义
type MyType = { id: string, name: string }
// 3. 响应式变量声明
const data = ref<Array<MyType>>([])
const loading = ref<boolean>(false)
// 4. 工具函数(不依赖其他函数的)
const getUserId = (): string => { ... }
// 5. 业务函数(可能依赖工具函数)
const loadData = async () => { ... }
const resetData = () => { ... }
// 6. 事件处理函数
const handleClick = () => { ... }
// 7. 生命周期钩子和 watch放在最后
watch(data, () => { ... })
onMounted(() => { ... })
</script>
```
4. 编译错误提示
- 错误信息:"找不到名称 xxx"
- 原因:函数在调用之后定义
- 解决:调整函数定义顺序,确保被调用的函数先定义
================================================================================
十四、箭头函数限制(重要)
================================================================================
1. 箭头函数不支持默认参数值
- 错误示例:
```typescript
const loadData = async (page: number = 1) => { ... } // 错误!
```
- 正确示例:
```typescript
// 方案1不使用默认参数调用时显式传参
const loadData = async (page: number): Promise<void> => { ... }
loadData(1) // 调用时传参
// 方案2使用普通函数定义
function loadData(page: number = 1): Promise<void> { ... }
```
2. 箭头函数需要明确返回类型
- 建议显式声明返回类型:`: Promise<void>` 或 `: string` 等
- 示例:`const getData = async (): Promise<string> => { ... }`
3. 编译错误提示
- 错误信息:"Anonymous functions cannot specify default values for their parameters"
- 原因:箭头函数使用了默认参数
- 解决:移除默认参数,改用显式传参或普通函数定义
================================================================================
十五、数组元素属性访问(重要)
================================================================================
1. Array<any> 元素属性访问问题
- 数组元素类型为 any 时,不能直接访问属性
- 错误示例:
```typescript
const steps: Array<any> = [...]
steps[1].time = 'xxx' // 错误!找不到名称 "time"
```
- 正确示例:
```typescript
const steps: Array<any> = [...]
const step = steps[1] as UTSJSONObject
step.set('time', 'xxx')
```
2. 推荐方案:定义明确的类型
```typescript
type StepType = { status: number, title: string, time: string, desc: string }
const steps: Array<StepType> = [...]
steps[1].time = 'xxx' // 正确!
```
3. 编译错误提示
- 错误信息:"找不到名称 xxx"
- 原因any 类型数组元素无法直接访问属性
- 解决:转换为 UTSJSONObject 或定义明确类型
================================================================================
十六、类型定义与可空类型(重要)
================================================================================
1. 类型定义中可空类型的处理
- 如果属性可能为 null必须显式声明为可空类型
- 错误示例:
```typescript
type ItemType = {
sku_specifications: any // 错误!如果值可能为 null
}
const spec = getSpec() // 返回 Any?
item.sku_specifications = spec // 类型不匹配
```
- 正确示例:
```typescript
type ItemType = {
sku_specifications: any | null // 正确!
}
const specRaw = getSpec()
const spec = (specRaw != null) ? (specRaw as any) : null
item.sku_specifications = spec
```
2. UTSJSONObject.get() 返回值处理
- get() 方法返回 Any? 类型
- 必须先判断 null再进行类型转换
- 示例:
```typescript
const raw = obj.get('field')
const value = (raw != null) ? (raw as string) : null
```
3. 编译错误提示
- 错误信息:"参数类型不匹配:实际类型为 'Any?',预期类型为 'Any'"
- 原因:可空类型不能直接赋值给非空类型
- 解决:修改类型定义为可空类型,或处理 null 情况
================================================================================
十七、模板中的可空类型处理(重要)
================================================================================
1. 模板中可选链限制
- 模板中 ?.length 等可选链操作可能报错
- 错误示例:
```html
<view v-if="refund.status_history?.length > 0">
```
- 正确示例:
```html
<view v-if="refund.status_history != null && refund.status_history.length > 0">
```
2. 编译错误提示
- 错误信息:"Operator call is prohibited on a nullable receiver of type 'Number?'"
- 原因:可空类型不能直接调用操作符
- 解决:显式判断 null 后再访问属性
================================================================================
十八、解构赋值限制(重要)
================================================================================
1. UTS 中解构赋值可能有问题
- 错误示例:
```typescript
const { data, error } = await supa.from('table').select('*')
```
- 正确示例:
```typescript
const response = await supa.from('table').select('*').execute()
const data = response.data
const error = response.error
```
2. 推荐使用 .execute() 获取完整响应
- 使用 response.data 和 response.error 访问结果
- 避免使用解构赋值
================================================================================
十九、API 响应数据处理(重要)
================================================================================
1. response.data 返回 Any? 类型
- 错误示例:
```typescript
order.value = orderRes.data // 错误Any? 不能赋值给 Any
const itemsArray = itemsRes.data ?? [] // 错误!无法推断空数组类型
```
- 正确示例:
```typescript
if (orderRes.data != null) {
order.value = orderRes.data as UTSJSONObject
}
const rawData = itemsRes.data
let itemsArray: Array<any> = []
if (rawData != null) {
itemsArray = rawData as Array<any>
}
```
2. 空数组类型推断问题
- `[] ?? []` 或 `data ?? []` 无法推断类型
- 必须显式声明数组类型
- 示例:
```typescript
// 错误
const arr = data ?? []
// 正确
let arr: Array<any> = []
if (data != null) {
arr = data as Array<any>
}
```
3. 编译错误提示
- 错误信息:"Assignment type mismatch: actual type is 'Any?', but 'Any' was expected"
- 原因:可空类型 Any? 不能直接赋值给非空类型
- 解决:先判断 null再进行类型转换
- 错误信息:"Cannot infer type for this parameter" 或 "Not enough information to infer type"
- 原因:空数组无法推断类型
- 解决:显式声明数组类型
================================================================================
二十、ref 对象字面量类型(重要)
================================================================================
1. ref 对象字面量必须定义类型
- 错误示例:
```typescript
const merchantRating = ref({
description: 5,
logistics: 5,
service: 5
})
merchantRating.value.description = rating // 错误!找不到名称 "description"
```
- 正确示例:
```typescript
type MerchantRatingType = {
description: number
logistics: number
service: number
}
const merchantRating = ref<MerchantRatingType>({
description: 5,
logistics: 5,
service: 5
} as MerchantRatingType)
merchantRating.value.description = rating // 正确!
```
2. 编译错误提示
- 错误信息:"找不到名称 xxx"
- 原因ref 对象字面量没有显式类型定义
- 解决:定义 type 并在 ref 中指定泛型类型
================================================================================
二十一、if 条件与可空类型(重要)
================================================================================
1. if 条件必须是 boolean 类型
- 错误示例:
```typescript
if (merchant.value) { ... } // 错误MerchantType? 不是 boolean
```
- 正确示例:
```typescript
if (merchant.value != null) { ... } // 正确!
```
2. 可空类型不能直接用于 if 条件
- 必须使用 != null 或 !== null 判断
- 示例:
```typescript
// 错误
if (obj) { ... }
// 正确
if (obj != null) { ... }
```
3. 编译错误提示
- 错误信息:"Condition type mismatch: inferred type is 'XXX?' but 'Boolean' was expected"
- 原因:可空类型不能直接用于 if 条件
- 解决:使用 != null 判断
================================================================================
二十二、throw 语句限制(重要)
================================================================================
1. throw 语句不能抛出 Any 类型
- 错误示例:
```typescript
const { error } = await supa.from('table').insert(data)
if (error !== null) {
throw error // 错误Any 类型不能抛出
}
```
- 正确示例:
```typescript
const res = await supa.from('table').insert(data).execute()
if (res.error != null) {
console.error('操作失败:', res.error)
uni.showToast({ title: '操作失败', icon: 'none' })
return // 处理错误而非抛出
}
```
2. 推荐错误处理方式
- 记录错误日志
- 显示用户提示
- 返回或终止操作
3. 编译错误提示
- 错误信息:"类型不匹配: 推断类型是'Any',,但预期的是'Throwable'"
- 原因UTS 中 throw 只能抛出 Throwable 类型
- 解决:处理错误而非抛出,或创建 Error 对象
================================================================================
二十三、模板中的可选链与属性访问(重要)
================================================================================
1. 模板中可选链限制
- 错误示例:
```html
<text>{{ order?.order_no }}</text>
<text>{{ formatTime(order?.created_at) }}</text>
```
- 正确示例:
```html
<text>{{ order != null ? order.order_no : '' }}</text>
<text>{{ formatTime(order != null ? order.created_at : '') }}</text>
```
2. ref<any> 在模板中无法访问属性
- 错误示例:
```typescript
const order = ref<any>({})
```
```html
<text>{{ order?.order_no }}</text> <!-- 错误!找不到名称 -->
```
- 正确示例:
```typescript
type OrderType = {
id: string
order_no: string
created_at: string
}
const order = ref<OrderType | null>(null)
```
```html
<text>{{ order != null ? order.order_no : '' }}</text>
```
3. 编译错误提示
- 错误信息:"找不到名称 xxx"
- 原因any 类型或可选链在模板中无法正确推断属性
- 解决:定义明确类型,使用三元表达式替代可选链
================================================================================
二十四、数组操作限制(重要)
================================================================================
1. 展开运算符 [...arr] 不支持
- 错误示例:
```typescript
const shuffled = [...allGuessItems.value]
searchResults.value.push(...newItems)
```
- 正确示例:
```typescript
// 复制数组
const arr: Array<any> = []
for (let i: number = 0; i < allGuessItems.value.length; i++) {
arr.push(allGuessItems.value[i])
}
// 追加元素
for (let i: number = 0; i < newItems.length; i++) {
searchResults.value.push(newItems[i])
}
```
2. Array.from(new Set()) 不支持
- 错误示例:
```typescript
const uniqueNames = Array.from(new Set(names))
```
- 正确示例:
```typescript
// 手动去重
const uniqueNames: Array<string> = []
for (let i: number = 0; i < names.length; i++) {
let found = false
for (let j: number = 0; j < uniqueNames.length; j++) {
if (uniqueNames[j] === names[i]) {
found = true
break
}
}
if (found === false) {
uniqueNames.push(names[i])
}
}
```
3. .sort() 带回调函数不支持
- 错误示例:
```typescript
const shuffled = arr.sort(() => Math.random() - 0.5)
```
- 正确示例:
```typescript
// Fisher-Yates 洗牌算法
for (let i: number = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
const temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
```
================================================================================
二十五、异步操作限制(重要)
================================================================================
1. Promise.all 可能有问题
- 错误示例:
```typescript
const [prodResp, shopResp] = await Promise.all([
supabaseService.searchProducts(keyword, page, limit),
supabaseService.searchShops(keyword)
])
```
- 正确示例:
```typescript
const prodResp = await supabaseService.searchProducts(keyword, page, limit)
const shopResp = await supabaseService.searchShops(keyword)
```
2. 解构赋值可能有问题
- 错误示例:
```typescript
const [prodResp, shopResp] = await Promise.all([...])
```
- 正确示例:
```typescript
const prodResp = await func1()
const shopResp = await func2()
```
================================================================================
二十六、事件对象处理(重要)
================================================================================
1. 事件对象属性访问
- 错误示例:
```typescript
const onInput = (e: any) => {
const val = e.detail.value // 错误!找不到名称 "detail"
}
```
- 正确示例:
```typescript
const onInput = (e: any) => {
const eObj = e as UTSJSONObject
const detailRaw = eObj.get('detail')
const detail = detailRaw != null ? (detailRaw as UTSJSONObject) : (new UTSJSONObject())
const val = detail.getString('value') ?? ''
}
```
2. 编译错误提示
- 错误信息:"找不到名称 xxx"
- 原因any 类型事件对象无法直接访问属性
- 解决:转换为 UTSJSONObject 后使用 get() 方法
================================================================================
二十七、数组索引访问限制(重要)
================================================================================
1. 数组索引访问可能越界
- 错误示例:
```typescript
const texts = ['非常差', '差', '一般', '好', '非常好']
return texts[rating - 1] ?? '未评价' // 可能越界或类型推断失败
```
- 正确示例:
```typescript
if (rating === 1) return '非常差'
if (rating === 2) return '差'
if (rating === 3) return '一般'
if (rating === 4) return '好'
if (rating === 5) return '非常好'
return '未评价'
```
2. 推荐使用 if-else 或 Map 替代数组索引查找
================================================================================
新增:
getSpecText 函数 - 将 Object.keys() 和 .map() 替换为 JSON.stringify()String() 替换为 as string 类型转换
canSubmit 计算属性 - 将 .every() 替换为 for 循环遍历
模板中的取反操作符 - 将 !canSubmit 替换为 canSubmit === false!isSubmitting 替换为 isSubmitting === false
订单商品数据处理 - 将 .map() 和展开运算符 ...item 替换为 for 循环和手动属性赋值
setRating 函数 - 将 [...ratings.value] 替换为手动数组复制
setMerchantRating 函数 - 将 { ...merchantRating.value } 替换为手动对象复制,参数类型改为 string
toggleAnonymous 函数 - 将 event.detail.value 改为 UTSJSONObject 访问方式
uploadImage 函数 - 将 ...tempFiles 和 [...images.value] 替换为 for 循环
deleteImage 函数 - 将 [...images.value] 替换为手动数组复制
submitReview 函数 - 将 .map() 替换为 for 循环构建评价数据
OrderItemType 类型定义 - 添加了 order_id 字段
getCurrentUserId 函数 - 将可选链替换为显式 null 检查和 UTSJSONObject 转换
================================================================================
十五、整理时间2026-02-25 settings.uvue 修复新增
================================================================================
1. Storage 数据类型转换
- uni.getStorageSync() 返回 any 类型,不能直接赋值给强类型变量
- 需要转换为 UTSJSONObject 后使用 getString()、getBoolean() 等方法
- 示例:
```typescript
// 错误
const userStore = uni.getStorageSync('userInfo')
if (userStore) {
userInfo.value = userStore // 类型不匹配
}
// 正确
const userStore = uni.getStorageSync('userInfo')
if (userStore != null) {
const storeObj = userStore as UTSJSONObject
userInfo.value = {
id: storeObj.getString('id') ?? '',
phone: storeObj.getString('phone'),
email: storeObj.getString('email')
}
}
```
2. 可选链操作符限制
- UTS Android 不支持 obj?.property 可选链操作符用于条件判断
- 需要使用显式 null 检查
- 示例:
```typescript
// 错误
if (appInfo?.appVersion) { ... }
// 正确
if (appInfo != null) {
const infoObj = appInfo as UTSJSONObject
const version = infoObj.getString('appVersion')
if (version != null) { ... }
}
```
3. 布尔值取反操作
- 不支持 !variable 取反操作
- 需要使用 variable === false 显式判断
- 示例:
```typescript
// 错误
notifications.value[type] = !notifications.value[type]
// 正确
if (type === 'order') {
notifications.value.order = notifications.value.order === false
}
```
4. keyof typeof 限制
- 不支持 keyof typeof 语法用于动态属性访问
- 需要使用字符串参数和条件判断
- 示例:
```typescript
// 错误
const toggleNotification = (type: keyof NotificationType) => {
notifications.value[type] = !notifications.value[type]
}
// 正确
const toggleNotification = (type: string) => {
if (type === 'order') {
notifications.value.order = notifications.value.order === false
} else if (type === 'promotion') {
notifications.value.promotion = notifications.value.promotion === false
}
}
```
5. userInfo.value 属性访问
- userInfo.value 是强类型 UserType不是 UTSJSONObject
- 不能使用 getString() 方法,应直接访问属性
- 示例:
```typescript
// 错误
let userId = userInfo.value.getString('id')
// 正确
let userId: string | null = userInfo.value.id
```
================================================================================
十六、整理时间2026-02-25 shop-detail.uvue 修复新增
================================================================================
1. 页面参数获取
- getCurrentPages() 返回的 options 需要转换为 UTSJSONObject
- 使用 getString() 方法获取参数
- 示例:
```typescript
// 错误
const options = pages[pages.length - 1].options as any
const mId = options['merchantId']
// 正确
const options = pages[pages.length - 1].options as UTSJSONObject
const mId = options.getString('merchantId')
```
2. 条件判断显式检查
- if (paramId) 需要改为 if (paramId != null && paramId !== '')
- if (userId) 需要改为 if (userId != null && userId !== '')
- !isLoading.value 需要改为 isLoading.value === false
- 示例:
```typescript
// 错误
if (hasMore.value && !isLoading.value && currentMerchantId.value != '')
// 正确
if (hasMore.value && isLoading.value === false && currentMerchantId.value != '')
```
3. typeof === 'function' 替换为 try-catch
- 不支持 typeof xxx === 'function' 检查
- 使用 try-catch 包裹方法调用
- 示例:
```typescript
// 错误
if (typeof supabaseService.fetchShopCoupons === 'function') {
coupons.value = await supabaseService.fetchShopCoupons(id)
}
// 正确
try {
// @ts-ignore
coupons.value = await supabaseService.fetchShopCoupons(id)
} catch(e) {
console.warn('Method not available')
}
```
4. .map() 替换为 for 循环
- 不支持 .map() 数组方法
- 使用 for 循环遍历并构建新数组
- 示例:
```typescript
// 错误
const list = rawList.map((item): ProductType => { ... })
// 正确
const list: ProductType[] = []
for (let idx: number = 0; idx < rawList.length; idx++) {
const item = rawList[idx] as UTSJSONObject
const product: ProductType = { ... }
list.push(product)
}
```
5. 展开运算符替换
- 不支持 ...arr 展开运算符
- 使用 for 循环逐个添加元素
- 示例:
```typescript
// 错误
images.push(...arr)
products.value.push(...list)
// 正确
for (let i: number = 0; i < arr.length; i++) {
images.push(arr[i])
}
for (let i: number = 0; i < list.length; i++) {
products.value.push(list[i])
}
```
6. any 类型属性访问
- any 类型不能直接访问属性
- 转换为 UTSJSONObject 后使用 getString()、getNumber() 方法
- 示例:
```typescript
// 错误
const safeItem = item as any
let safePrice = safeItem['base_price'] as number
// 正确
const item = rawList[idx] as UTSJSONObject
let safePrice = item.getNumber('base_price')
```
7. typeof string 检查替换
- 不支持 typeof rawUrl === 'string' 检查
- 直接尝试类型转换或使用 instanceof
- 示例:
```typescript
// 错误
if (typeof rawUrl === 'string') { ... }
// 正确
// 直接尝试使用,或使用其他方式判断
const rawUrlStr = imageUrlsRaw as string
if (rawUrlStr.startsWith('[')) { ... }
```
================================================================================
十七、整理时间2026-02-25 wallet.uvue 修复新增
================================================================================
1. isNaN 函数限制
- UTS Android 不支持 isNaN() 函数
- 使用显式范围检查替代
- 示例:
```typescript
// 错误
const amount = parseFloat(rechargeAmount.value)
return !isNaN(amount) && amount >= 10 && amount <= 5000
// 正确
const amount = parseFloat(rechargeAmount.value)
if (amount == null || amount < 10 || amount > 5000) {
return false
}
return true
```
2. Record 类型限制
- UTS Android 不支持 Record<K, V> 类型
- 使用条件判断替代对象查找
- 示例:
```typescript
// 错误
const icons: Record<string, string> = {
recharge: '💳',
consume: '🛒'
}
const icon = icons[type]
// 正确
const getTransactionIcon = (type: string): string => {
if (type === 'recharge') return '💳'
if (type === 'consume') return '🛒'
return '💰'
}
```
3. for 循环变量类型
- for 循环的索引变量必须显式声明类型
- 示例:
```typescript
// 错误
for (let i = 0; i < data.length; i++) { ... }
// 正确
for (let i: number = 0; i < data.length; i++) { ... }
```
4. 展开运算符替换
- 不支持 ...arr 展开运算符
- 使用 for 循环逐个添加
- 示例:
```typescript
// 错误
transactions.value.push(...mappedData)
// 正确
for (let i: number = 0; i < mappedData.length; i++) {
transactions.value.push(mappedData[i])
}
```
5. 可选链操作符替换
- 不支持 obj?.method() 可选链
- 使用显式 null 检查
- 示例:
```typescript
// 错误
return userStore?.getString('id') ?? ''
// 正确
if (userStore == null) return ''
const userInfo = userStore as UTSJSONObject
return userInfo.getString('id') ?? ''
```
6. 模板中的取反操作符
- 模板中不支持 !variable 取反
- 使用 === false 显式判断
- 示例:
```html
<!-- 错误 -->
<view v-if="transactions.length === 0 && !isLoading">
<!-- 正确 -->
<view v-if="transactions.length === 0 && isLoading === false">
```
7. 条件判断中的取反操作符
- 不支持 !hasMore.value 取反
- 使用 hasMore.value === false 显式判断
- 示例:
```typescript
// 错误
if (hasMore.value && !isLoading.value) { ... }
// 正确
if (hasMore.value && isLoading.value === false) { ... }
```
================================================================================
十八、整理时间2026-02-25 withdraw.uvue 修复新增
================================================================================
1. 模板中的可选链操作符
- 模板中不支持 obj?.property 可选链
- 使用 v-if 判断后再访问属性
- 示例:
```html
<!-- 错误 -->
<text>{{ selectedBank?.bank_name }}</text>
<text v-if="selectedBank?.id == item.id">✓</text>
<!-- 正确 -->
<view v-if="selectedBank != null">
<text>{{ selectedBank.bank_name }}</text>
</view>
<text v-if="selectedBank != null && selectedBank.id == item.id">✓</text>
```
2. 按钮禁用状态
- :disabled="!isValid" 不支持取反
- 使用 :disabled="isValid === false"
- 示例:
```html
<!-- 错误 -->
<button :disabled="!isValid">提交</button>
<!-- 正确 -->
<button :disabled="isValid === false">提交</button>
```
3. Map 类型访问限制
- UTS Android 不支持 Map<K, V> 类型的 get() 方法访问属性
- 应统一使用 UTSJSONObject
- 示例:
```typescript
// 错误
const m = item as Map<string, any>
const idVal = m.get('id')
// 正确
const itemObj = item as UTSJSONObject
const id = itemObj.getString('id') ?? ''
```
4. 函数内取反操作符
- if (!isValid.value) 不支持取反
- 使用 if (isValid.value === false)
- 示例:
```typescript
// 错误
if (!isValid.value) return
// 正确
if (isValid.value === false) return
```
================================================================================
二十一、2026-02-25 user 目录页面修复记录
================================================================================
1. change-password.uvue 修复
- 问题:!oldPassword.value 取反操作不支持
- 修复:改为 oldPassword.value == '' 显式判断
- 问题const { error } = await ... 解构赋值不支持
- 修复:改为 const result = await ... 然后 result.error 访问
2. login.uvue 修复
- 问题as unknown as number 双重类型转换不支持
- 修复:改为 as number 单一类型转换
- 问题typeof err === 'object' 不支持
- 修复:使用 try-catch 包裹类型转换
3. forgot-password.uvue 修复
- 问题:!emailRegex.test(this.email) 取反操作不支持
- 修复:改为 emailRegex.test(this.email) == false
- 问题typeof err === 'object' 不支持
- 修复:使用 try-catch 包裹类型转换
4. register.uvue 修复
- 问题:!protocol.value 取反操作不支持
- 修复:改为 protocol.value == false
- 问题:!validateEmail() 等取反操作不支持
- 修复:改为 validateEmail() == false
================================================================================
二十二、常见修复模式速查
================================================================================
1. 取反操作修复模式
```typescript
// 错误
if (!variable) { ... }
if (!isValid.value) { ... }
if (!validate()) { ... }
// 正确 - 根据类型选择
if (variable == null || variable == '') { ... } // 字符串判空
if (isValid.value == false) { ... } // 布尔值取反
if (validate() == false) { ... } // 函数返回布尔值取反
```
2. 解构赋值修复模式
```typescript
// 错误
const { data, error } = await someAsyncCall()
// 正确
const result = await someAsyncCall()
const data = result.data
const error = result.error
```
3. typeof 检查修复模式
```typescript
// 错误
if (typeof err === 'object') { ... }
if (typeof xxx === 'function') { ... }
// 正确
try {
const e = err as Error
// 使用 e
} catch (e2) {
// 处理转换失败
}
```
4. as unknown as 修复模式
```typescript
// 错误
const timer = setInterval(...) as unknown as number
// 正确
const timer = setInterval(...) as number
```
================================================================================
二十三、错误处理最佳实践
================================================================================
1. 统一错误处理模式
```typescript
try {
const result = await someAsyncCall()
if (result.error != null) {
const errorMsg = (result.error as Error).message
uni.showToast({ title: errorMsg, icon: 'none' })
return
}
// 处理成功结果
} catch (e) {
console.error('操作失败:', e)
uni.showToast({ title: '操作失败', icon: 'none' })
}
```
2. 可空类型安全访问
```typescript
// 安全访问对象属性
const value = obj != null ? obj.property : null
// 安全调用方法
const result = obj != null ? obj.method() : null
```
3. 数组安全访问
```typescript
// 安全访问数组元素
if (arr.length > index) {
const item = arr[index]
// 使用 item
}
```
================================================================================
文档结束
================================================================================