Files
medical-mall/pages/mall/consumer/doc/uts.txt
2026-02-27 16:51:56 +08:00

1992 lines
74 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
}
```
================================================================================
文档结束
================================================================================
================================================================================
二十四、2026-02-27 函数可选参数限制(重要)
================================================================================
1. 可选参数不能跳过传递
- UTS Android 不支持跳过可选参数传递
- 如果函数有多个可选参数,必须按顺序传递所有参数
- 错误示例:
```typescript
// 函数定义
async addToCart(productId: string, quantity: number = 1, skuId?: string, merchantId?: string): Promise<boolean>
// 错误调用 - 跳过了 merchantId 参数
await supabaseService.addToCart(productId, 1, '')
// 编译错误No value passed for parameter 'merchantId'
```
- 正确示例:
```typescript
// 方案1给可选参数添加默认值
async addToCart(productId: string, quantity: number = 1, skuId: string = '', merchantId: string = ''): Promise<boolean>
// 方案2调用时传递所有参数
await supabaseService.addToCart(productId, 1, '', '')
```
2. 可选参数定义规范
- 推荐使用 `param: Type = defaultValue` 而非 `param?: Type`
- `param?: Type` 在 Android 端调用时仍需传递参数
- `param: Type = defaultValue` 可以在不传参时使用默认值
- 示例:
```typescript
// 不推荐 - 调用时仍需传递参数
function foo(a: string, b?: string, c?: string): void
// 推荐 - 可以跳过参数使用默认值
function foo(a: string, b: string = '', c: string = ''): void
```
3. 编译错误提示
- 错误信息:"No value passed for parameter 'xxx'"
- 原因:可选参数在 Android 端不能跳过
- 解决:
1. 修改函数签名,使用默认值 `param: Type = defaultValue`
2. 调用时传递所有参数
4. 最佳实践
- 对于有多个可选参数的函数,统一使用默认值语法
- 调用时显式传递所有参数,避免依赖可选参数跳过
- 在服务层函数定义中,优先使用 `= ''` 或 `= 0` 等默认值
================================================================================
二十五、2026-02-27 模板中的非空断言限制(重要)
================================================================================
1. 模板中不支持非空断言操作符 `!`
- UTS Android 模板中不能使用 `variable!` 非空断言
- 错误示例:
```html
<text v-if="product.original_price != null && product.original_price! > product.price">
```
- 正确示例:
```html
<text v-if="product.original_price != null && product.original_price > product.price">
```
2. 编译错误提示
- 错误信息:"参数类型不匹配:实际类型为 'Number?',预期类型为 'Number'"
- 原因:模板中使用非空断言 `!` 不被支持
- 解决:移除非空断言 `!`,直接使用变量进行比较
3. 最佳实践
- 在模板中,先用 `!= null` 判断可空类型,然后直接使用变量
- UTS 编译器会在 `!= null` 判断后自动识别变量为非空类型
================================================================================
二十六、2026-02-27 未导入类型的处理(重要)
================================================================================
1. 未导入的类型不能直接使用
- 在页面中使用的类型必须先导入或使用 UTSJSONObject 替代
- 错误示例:
```typescript
// Shop 类型未导入
const s = shopRespData[i] as Shop
const id = s.id // 找不到名称 "id"
```
- 正确示例:
```typescript
// 使用 UTSJSONObject
const s = shopRespData[i] as UTSJSONObject
const id = s.getString('id') ?? ''
const name = s.getString('shop_name') ?? ''
```
2. 编译错误提示
- 错误信息:"找不到名称 'XXX'"
- 原因:类型未导入或类型定义不存在
- 解决:
1. 导入需要的类型:`import { Shop } from '@/utils/supabaseService.uts'`
2. 使用 UTSJSONObject 替代:`as UTSJSONObject` 然后用 `getString()`、`getNumber()` 访问属性
3. 最佳实践
- 对于简单的数据转换,推荐使用 UTSJSONObject
- 避免在多个文件中重复定义相同的类型
- 如果需要类型安全,从服务层导入类型定义
================================================================================
二十七、2026-02-27 服务层数据字段完整性(重要)
================================================================================
1. 服务层返回数据必须包含所有必要字段
- 从数据库获取数据时,必须正确映射所有需要的字段
- 错误示例:
```typescript
const product: Product = {
id: prodObj.getString('id') ?? '',
name: prodObj.getString('name') ?? '',
// 错误merchant_id 硬编码为空字符串
merchant_id: ''
} as Product
```
- 正确示例:
```typescript
const product: Product = {
id: prodObj.getString('id') ?? '',
name: prodObj.getString('name') ?? '',
// 正确:从数据库获取 merchant_id
merchant_id: prodObj.getString('merchant_id') ?? ''
} as Product
```
2. 调用服务层方法时必须传递完整参数
- 页面调用服务层方法时,需要传递所有必要参数
- 错误示例:
```typescript
// 错误merchant_id 传空字符串
await supabaseService.addToCart(productId, 1, '', '')
```
- 正确示例:
```typescript
// 正确:从商品对象获取 merchant_id
const merchantId = product.merchant_id ?? ''
await supabaseService.addToCart(productId, 1, '', merchantId)
```
3. 编译错误提示
- 问题表现:数据添加到数据库失败,或添加的数据不完整
- 原因:服务层或页面层缺少必要字段的传递
- 解决:
1. 检查服务层数据映射是否完整
2. 检查页面调用时是否传递了所有必要参数
4. 最佳实践
- 服务层方法返回的对象应包含数据库视图的所有字段
- 页面调用服务层方法时,应从数据对象中获取并传递所有参数
- 对于关联数据(如 merchant_id确保在数据加载时一并获取
================================================================================
二十八、2026-02-27 模板中的非运算符限制(重要)
================================================================================
1. 模板中不支持 `!` 非运算符
- UTS Android 模板中不能使用 `!variable` 非运算符
- 错误示例:
```html
<view v-if="!brand.logo_url">
```
- 正确示例:
```html
<view v-if="brand.logo_url == null || brand.logo_url == ''">
```
2. 编译错误提示
- 错误信息:"找不到名称'not'"
- 原因:模板中不支持非运算符 `!`
- 解决:使用显式的比较表达式替代
3. 最佳实践
- 使用 `== null` 或 `== ''` 检查空值
- 使用 `!= null && != ''` 检查非空值
================================================================================
二十九、2026-02-27 索引访问限制(重要)
================================================================================
1. 不支持 `(obj as any)['key']` 索引访问方式
- UTS Android 不支持对 any 类型使用索引访问
- 错误示例:
```typescript
const detail = (e as any)['detail']
val = detail['value'] ?? ''
```
- 正确示例:
```typescript
// 方案1使用 UTSJSONObject
const eObj = JSON.parse(JSON.stringify(e)) as UTSJSONObject
const detail = eObj.get('detail') as UTSJSONObject
val = detail.getString('value') ?? ''
// 方案2先判断类型再转换
if (e instanceof UTSJSONObject) {
const eObj = e as UTSJSONObject
const detail = eObj.get('detail') as UTSJSONObject
val = detail.getString('value') ?? ''
}
```
2. 编译错误提示
- 错误信息:"Unresolved reference. None of the following candidates is applicable because of a receiver type mismatch"
- 原因any 类型不支持索引访问
- 解决:转换为 UTSJSONObject 后使用 `.get()` 方法
3. 最佳实践
- 统一使用 UTSJSONObject 处理动态对象
- 使用 `.get()`、`.getString()`、`.getNumber()` 方法访问属性
- 对于复杂对象,先用 `JSON.parse(JSON.stringify(obj))` 转换
================================================================================
三十、2026-02-27 字符串不能直接作为布尔条件(重要)
================================================================================
1. 字符串不能直接作为 if 条件
- UTS Android 不支持将字符串直接作为布尔条件判断
- 错误示例:
```typescript
const paramId = '123'
if (paramId) { // 错误:字符串不能直接作为布尔条件
// ...
}
```
- 正确示例:
```typescript
const paramId = '123'
if (paramId != null && paramId != '') { // 正确:显式判断
// ...
}
```
2. 编译错误提示
- 错误信息:"Condition type mismatch: inferred type is 'String' but 'Boolean' was expected"
- 原因:字符串类型不能直接作为布尔条件
- 解决:使用显式的比较表达式
3. 最佳实践
- 使用 `!= null && != ''` 检查字符串非空
- 使用 `== null || == ''` 检查字符串为空
================================================================================
三十一、2026-02-27 函数定义顺序(重要)
================================================================================
1. 函数必须在调用前定义
- UTS Android 要求函数在调用之前完成定义
- 这与 JavaScript 的函数提升不同
- 错误示例:
```typescript
onMounted(() => {
loadData() // 错误loadData 还未定义
})
const loadData = async () => {
// ...
}
```
- 正确示例:
```typescript
const loadData = async () => {
// ...
}
onMounted(() => {
loadData() // 正确loadData 已定义
})
```
2. 编译错误提示
- 错误信息:"找不到名称'xxx'"
- 原因:函数在调用点之后定义
- 解决:将函数定义移到调用之前
3. 最佳实践
- 将所有函数定义放在生命周期钩子onMounted、onShow 等)之前
- 按依赖关系排序函数定义顺序
================================================================================
三十二、2026-02-27 联合类型属性访问(重要)
================================================================================
1. 联合类型不能直接访问属性
- 当参数类型为联合类型(如 `A | B`)时,不能直接访问属性
- 错误示例:
```typescript
type A = { id: string, name: string }
type B = { id: string, title: string }
const foo = (item: A | B) => {
const id = item.id // 错误:联合类型不能直接访问属性
}
```
- 正确示例:
```typescript
const foo = (item: A | B) => {
// 方案1转换为 UTSJSONObject
const obj = JSON.parse(JSON.stringify(item)) as UTSJSONObject
const id = obj.getString('id') ?? ''
// 方案2使用类型守卫
if ('name' in item) {
const id = item.id // 此时类型已收窄为 A
}
}
```
2. 编译错误提示
- 错误信息:"找不到名称'xxx'"
- 原因:联合类型的属性访问受限
- 解决:转换为 UTSJSONObject 或使用类型守卫
3. 最佳实践
- 对于联合类型参数,统一转换为 UTSJSONObject 处理
- 使用 `.getString()`、`.getNumber()` 等方法安全访问属性
================================================================================
三十三、2026-02-27 any 类型变量不能赋值为 null重要
================================================================================
1. any 类型变量不能赋值为 null
- UTS Android 中 `any` 类型不能赋值为 `null`
- 错误示例:
```typescript
let res: any = null // 错误Null cannot be a value of a non-null type 'Any'
```
- 正确示例:
```typescript
let res: any = {} // 正确:使用空对象
// 或者
let res: any | null = null // 使用联合类型
```
2. 编译错误提示
- 错误信息:"Null cannot be a value of a non-null type 'Any'"
- 原因any 类型不允许 null 值
- 解决:使用空对象 `{}` 或联合类型 `any | null`
================================================================================
三十四、2026-02-27 对象字面量类型推断问题(重要)
================================================================================
1. 对象字面量直接赋值给 ref 可能类型不匹配
- 当对象字面量直接赋值给特定类型的 ref 时,可能报类型不匹配错误
- 错误示例:
```typescript
merchant.value = {
id: shop.id,
user_id: shop.merchant_id,
// ...
} // 错误Assignment type mismatch
```
- 正确示例:
```typescript
// 方案1显式声明类型
const merchantData: MerchantType = {
id: shop.id,
user_id: shop.merchant_id,
// ...
}
merchant.value = merchantData
// 方案2使用 as 类型断言
merchant.value = {
id: shop.id,
user_id: shop.merchant_id,
// ...
} as MerchantType
```
2. 编译错误提示
- 错误信息:"Assignment type mismatch: actual type is '<anonymous>', but 'XXX' was expected"
- 原因:对象字面量被推断为匿名类型
- 解决:显式声明类型或使用类型断言
================================================================================
三十五、2026-02-27 any 类型不能直接访问属性(重要)
================================================================================
1. any 类型参数不能直接访问属性
- 在 map、forEach 等回调中any 类型的参数不能直接访问属性
- 错误示例:
```typescript
const list = rawList.map((item): ProductType => {
const id = item.id // 错误:找不到名称"id"
const name = item.name // 错误:找不到名称"name"
})
```
- 正确示例:
```typescript
const list = rawList.map((item: any): ProductType => {
// 方案1转换为 UTSJSONObject
const itemObj = JSON.parse(JSON.stringify(item)) as UTSJSONObject
const id = itemObj.getString('id') ?? ''
const name = itemObj.getString('name') ?? ''
// 方案2显式标注参数类型并使用索引
// 注意:这种方式在 UTS Android 中也可能有问题
})
```
2. 编译错误提示
- 错误信息:"找不到名称'xxx'"
- 原因any 类型的属性访问受限
- 解决:转换为 UTSJSONObject 后使用 `.getString()` 等方法
================================================================================
三十六、2026-02-27 类型断言不会添加方法(重要)
================================================================================
1. `as UTSJSONObject` 不会给对象添加方法
- 使用 `as UTSJSONObject` 只是类型断言,不会让普通对象获得 `getString` 等方法
- 错误示例:
```typescript
const profileObj = profile as UTSJSONObject
const id = profileObj.getString('user_id') // 运行时错误getString is not a function
```
- 正确示例:
```typescript
// 必须使用 JSON.parse(JSON.stringify()) 进行真正的转换
const profileObj = JSON.parse(JSON.stringify(profile)) as UTSJSONObject
const id = profileObj.getString('user_id') ?? ''
```
2. 运行时错误提示
- 错误信息:"XXX is not a function"
- 原因:类型断言只是编译时行为,不会改变运行时对象的方法
- 解决:使用 `JSON.parse(JSON.stringify())` 进行真正的对象转换
3. 最佳实践
- 对于从 API 返回的数据,统一使用 `JSON.parse(JSON.stringify())` 转换
- 使用 `instanceof UTSJSONObject` 检查对象类型
- 不要依赖 `as` 类型断言来添加方法
================================================================================
三十七、2026-02-27 类型必须包含所有必填字段(重要)
================================================================================
1. 创建类型实例时必须包含所有必填字段
- UTS 类型定义中的非可选字段(不带 `?`)都是必填的
- 错误示例:
```typescript
export type ProductType = {
id: string
merchant_id: string // 必填
category_id: string // 必填
name: string
// ...
}
// 错误:缺少 merchant_id、category_id 等必填字段
return {
id: item.id,
name: item.name,
price: item.price
} as ProductType // 运行时错误missing required property
```
- 正确示例:
```typescript
return {
id: itemObj.getString('id') ?? '',
merchant_id: itemObj.getString('merchant_id') ?? '',
category_id: itemObj.getString('category_id') ?? '',
name: itemObj.getString('name') ?? '未知商品',
description: itemObj.getString('description') ?? '',
images: images,
price: itemObj.getNumber('base_price') ?? 0,
original_price: itemObj.getNumber('market_price') ?? 0,
stock: itemObj.getNumber('total_stock') ?? 0,
sales: itemObj.getNumber('sale_count') ?? 0,
status: 1,
created_at: itemObj.getString('created_at') ?? ''
} as ProductType
```
2. 运行时错误提示
- 错误信息:"Failed to construct type, missing required property: xxx"
- 原因:类型定义中有必填字段未提供
- 解决:
1. 检查类型定义,确认所有必填字段
2. 为所有必填字段提供值,即使是空字符串或默认值
3. 最佳实践
- 查看类型定义,确认哪些字段是必填的(不带 `?`
- 使用 `??` 运算符提供默认值
- 对于可选字段,可以不提供或使用 `null`
================================================================================
三十八、2026-02-27 回调函数不能是 async重要
================================================================================
1. API 回调函数不能使用 async 修饰
- uni API 的回调函数(如 showModal 的 success不支持 async 函数
- 错误示例:
```typescript
uni.showModal({
title: '确认',
content: '确定要删除吗?',
success: async (res) => { // 错误:回调函数不能是 async
if (res.confirm) {
const result = await someAsyncFunction()
}
}
})
```
- 正确示例:
```typescript
uni.showModal({
title: '确认',
content: '确定要删除吗?',
success: (res) => {
if (res.confirm) {
// 使用 Promise.then() 代替 await
someAsyncFunction().then((result) => {
// 处理结果
})
}
}
})
```
2. 编译错误提示
- 错误信息:"参数类型不匹配:实际类型为 'Function1<..., UTSPromise<Unit>>',预期类型为 'Function1<..., Unit>?'"
- 原因:回调函数返回 Promise 而非 void
- 解决:使用 `.then()` 代替 `await`
3. 最佳实践
- 在回调函数中使用 `.then()` 处理异步操作
- 将异步逻辑封装为单独的函数,在回调中调用
================================================================================
三十九、2026-02-27 类型转换前必须检查类型(重要)
================================================================================
1. 使用 `as` 类型转换前必须检查实际类型
- 直接使用 `as string` 转换可能导致运行时类型转换异常
- 错误示例:
```typescript
const idVal = item['id']
const id = idVal as string // 错误:如果 idVal 是其他类型会崩溃
```
- 正确示例:
```typescript
const idVal = item['id']
const id = (idVal != null && typeof idVal == 'string') ? (idVal as string) : ''
```
2. 运行时错误提示
- 错误信息:"null cannot be cast to non-null type kotlin.String"
- 错误信息:"java.lang.Boolean cannot be cast to java.lang.String"
- 原因:直接类型转换时,实际类型与目标类型不匹配
- 解决:使用 `typeof` 检查类型后再转换
3. 最佳实践
- 使用 `typeof` 检查类型
- 使用 `!= null` 检查空值
- 提供默认值防止空指针异常
================================================================================
四十、2026-02-27 UTSJSONObject 必须正确转换(重要)
================================================================================
1. `as UTSJSONObject` 不会添加方法
- 从数据库返回的数据需要正确转换为 UTSJSONObject
- 错误示例:
```typescript
const item = rawList[i]
const brandObj = item as UTSJSONObject // 错误brandObj.getString 不存在
```
- 正确示例:
```typescript
const item = rawList[i]
const brandObj = JSON.parse(JSON.stringify(item)) as UTSJSONObject
const id = brandObj.getString('id') ?? ''
```
2. 运行时错误提示
- 错误信息:"getString is not a function"
- 原因:对象没有正确转换为 UTSJSONObject
- 解决:使用 `JSON.parse(JSON.stringify())` 进行转换
3. 最佳实践
- 对于从数据库/API 返回的数据,统一使用 `JSON.parse(JSON.stringify())` 转换
- 使用 `.getString()`、`.getNumber()` 等方法安全访问属性
================================================================================
================================================================================