1992 lines
74 KiB
Plaintext
1992 lines
74 KiB
Plaintext
================================================================================
|
||
UTS-Android 兼容性开发规范
|
||
================================================================================
|
||
> 以下为 uni-app-x (UTS) Android 端开发常见注意事项与踩坑点,建议所有开发成员遵循:
|
||
|
||
================================================================================
|
||
一、基础语法规范
|
||
================================================================================
|
||
|
||
1. 变量声明
|
||
- 只能使用 let 和 const,不能使用 var
|
||
- 变量声明必须有显式类型或初始化值
|
||
- 不支持 undefined 类型,变量未赋值就是 null
|
||
- 不支持 undefined 关键字,判断是否存在要用 != null
|
||
|
||
2. 类型定义
|
||
- 只适合转 type,不适合使用 interface(interface 在 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>() 方式
|
||
- 返回的是 result,resultdata 一般可以 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. 可空类型属性在模板中使用时需要处理 null:profile.gender ?? 'other'
|
||
97. 可空数字类型比较前需要先检查 null:profile.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 number,String(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()` 等方法安全访问属性
|
||
|
||
================================================================================
|
||
|
||
================================================================================
|