diff --git a/src/apps/desktop/src/api/terminal_api.rs b/src/apps/desktop/src/api/terminal_api.rs index a9f89f04c8..0006573436 100644 --- a/src/apps/desktop/src/api/terminal_api.rs +++ b/src/apps/desktop/src/api/terminal_api.rs @@ -13,7 +13,7 @@ use bitfun_core::service::remote_ssh::workspace_state::{ get_remote_workspace_manager, init_remote_workspace_manager, }; use bitfun_core::service::runtime::RuntimeManager; -use bitfun_core::service::config::load_terminal_env_vars; +use bitfun_core::service::config::{load_terminal_default_shell, load_terminal_env_vars}; use bitfun_core::service::terminal::TerminalEvent; use bitfun_core::service::terminal::{ AcknowledgeRequest as CoreAcknowledgeRequest, CloseSessionRequest as CoreCloseSessionRequest, @@ -81,6 +81,13 @@ impl TerminalState { } } + // Seed the default shell preference from the user's terminal + // configuration so new terminals and agent sessions honor the + // "Default Terminal" setting. Live updates after this point are + // applied via `SessionManager::update_default_shell` in the + // terminal config provider's `on_config_changed` handler. + config.default_shell = load_terminal_default_shell().await; + let api = TerminalApi::new(config).await; *api_guard = Some(api); *initialized = true; diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/README.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/README.md new file mode 100644 index 0000000000..707e402680 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/README.md @@ -0,0 +1,151 @@ +# HarmonyOS ArkTS 错误解决方案Skills + +> 专为 AI 辅助开发 HarmonyOS 应用设计的 用于解决ArkTS 编译错误和类型不匹配问题Skills + +## 📖 项目背景 + +在使用 AI 模型(如 Cursor、GitHub Copilot、Claude 等)开发 HarmonyOS 应用时,我们发现了一个普遍存在的问题: + +- **AI 模型会重复犯同样的 ArkTS 类型错误** +- **需要开发者反复手动修复相同的错误**,严重影响开发效率 + +例如,AI 经常生成: +- 使用全局 `animateTo` 而不是 `this.getUIContext().animateTo()` +- 使用 `window.Rect` 而不是 `window.TitleButtonRect` +- 访问 `TitleButtonRect` 不存在的 `left` 和 `top` 属性 +- 在 catch 子句中使用类型注解 +- 等等... + +为了解决这个问题,我们开发了这个 **ArkTS 错误解决方案库**,旨在: + +✅ **提高 AI 开发效率** - 让 AI 模型能够自动识别和修复常见错误 +✅ **减少重复工作** - 避免反复修复相同的类型错误 +✅ **提供最佳实践** - 每个错误都配有详细的解决方案和代码示例 + +## 🎯 项目目标 + +本仓库收集并整理了 **32+ 种常见的 ArkTS 编译错误和类型不匹配问题**,每个错误都包含: + +- 📝 详细的错误描述和原因分析 +- ✅ 正确的解决方案 +- 💡 最佳实践建议 +- 📚 完整的代码示例 + +## 📦 内容概览 + +### 错误分类 + +| 错误类型 | 数量 | 说明 | +|---------|------|------| +| **API 类型错误** | 5+ | Notification、Window、AppStorage 等 API 类型不匹配 | +| **对象类型错误** | 6+ | 对象展开、对象字面量、接口方法签名等 | +| **函数类型错误** | 4+ | 函数返回类型、箭头函数转换等 | +| **装饰器错误** | 2+ | @StorageLink 默认值、未使用变量警告等 | +| **其他类型错误** | 15+ | Catch 子句、ESObject、资源转换等 | + +### 主要错误类型 + +- ✅ **Notification API 类型错误** - ContentType 类型不兼容 +- ✅ **Window API 类型错误** - `window.getLastWindow` 类型推断问题 +- ✅ **AppStorage 类型错误** - `AppStorage.get()` 类型推断错误 +- ✅ **对象展开类型错误** - 对象展开时的类型推断问题 +- ✅ **@StorageLink 默认值错误** - 缺少默认值 +- ✅ **对象字面量接口错误** - 对象字面量缺少显式接口 +- ✅ **函数返回类型错误** - 返回类型推断受限 +- ✅ **箭头函数转换错误** - 使用函数表达式而非箭头函数 +- ✅ **TitleButtonRect 类型错误** - 返回类型错误和访问不存在属性 +- ✅ **Catch 子句类型错误** - catch 子句中的类型注解 +- ✅ **ESObject 类型错误** - ESObject 类型使用受限 +- ✅ **资源转换错误** - Resource 到 string/number 转换错误 +- ✅ 以及更多... + +## 🚀 使用方法 + +### 作为 AI Skill 使用 + +本仓库设计为 AI 开发工具的 Skill,可以直接被 AI 模型调用: + +1. **配置 Skill**:将本仓库添加到你的 AI 开发工具(如 Cursor)的 Skills 目录 +2. **自动识别**:AI 模型在生成代码时会自动参考这些解决方案 +3. **减少错误**:AI 生成的代码将更符合 HarmonyOS API 11+ 规范 + +### 手动查阅 + +你也可以直接查阅文档和代码示例: + +- 📚 **参考文档**:查看 `reference/` 目录下的详细错误说明 +- 💻 **代码示例**:查看 `assets/` 目录下的完整代码示例 + +## 📁 目录结构 + +``` +arkts-error-solution-skill/ +├── README.md # 本文件 +├── SKILL.md # Skill 配置文件 +├── assets/ # 代码示例目录 +│ ├── NotificationError.ets +│ ├── WindowTypeError.ets +│ ├── AppStorageError.ets +│ └── ... (27+ 个示例文件) +└── reference/ # 参考文档目录 + ├── notification_errors.md + ├── window_type_errors.md + ├── appstorage_errors.md + └── ... (27+ 个文档文件) +``` + +## 💡 使用示例 + +### 问题场景 + +AI 生成代码时经常出现这样的错误: + +```typescript +// ❌ AI 经常生成的错误代码 +async function getTitleButtonRect(context: UIAbilityContext): Promise { + const win = await window.getLastWindow(context); + const rect = win.getTitleButtonRect(); + return rect; // 类型错误! +} +``` + +### 解决方案 + +参考本仓库的解决方案,AI 可以生成正确的代码: + +```typescript +// ✅ 正确的代码 +async function getTitleButtonRect(context: UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + const titleButtonRect = win.getTitleButtonRect(); + resolve(titleButtonRect); // 正确! + }); + }); +} +``` + +## 🤝 贡献 + +欢迎提交 Issue 和 Pull Request! + +如果你发现了新的常见错误类型,或者有更好的解决方案,欢迎贡献: + +1. Fork 本仓库 +2. 创建你的特性分支 (`git checkout -b feature/AmazingError`) +3. 提交你的更改 (`git commit -m 'Add some AmazingError'`) +4. 推送到分支 (`git push origin feature/AmazingError`) +5. 开启一个 Pull Request + +## 📄 许可证 + +本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情 + +--- + +**让 AI 开发 HarmonyOS 应用更高效!** 🚀 + diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/SKILL.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/SKILL.md new file mode 100644 index 0000000000..0f6fd51237 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/SKILL.md @@ -0,0 +1,150 @@ +--- +name: arkts-error-fixes +description: Solutions for ArkTS compilation errors and type mismatches. Load this skill when compilation fails or when fixing ArkTS type errors. +--- + +# Harmony Error Fixes + +This skill provides solutions for common ArkTS compilation errors and type mismatches encountered during HarmonyOS development. + +## Error Categories + +| Category | Description | +|-----------|-------------| +| Notification API Type Errors | ContentType type incompatibility | +| Window API Type Errors | Type inference issues with `window.getLastWindow` | +| AppStorage Type Errors | Type inference errors with `AppStorage.get()` | +| Object Spread Type Errors | Type inference issues with object spread | +| @StorageLink Default Value Errors | Missing default values for `@StorageLink` properties | +| Object Literal Interface Errors | Object literals without explicit interfaces | +| Object Literal Type Errors | Using object literal types in return type annotations | +| Function Return Type Errors | Limited return type inference | +| Arrow Function Conversion Errors | Using function expressions instead of arrow functions | +| Color Property Errors | Non-existent `Color` properties | +| Interface Method Signature Errors | Method signature mismatches in object literals | +| AvoidArea Type Errors | Missing `visible` property in `AvoidArea` type | +| Standalone Function `this` Errors | Using `this` in standalone functions | +| TitleButtonRect Type Errors | Incorrect return type for `getTitleButtonRect`; accessing non-existent properties (left, top) | +| Catch Clause Type Errors | Type annotations in catch clauses | +| ESObject Type Errors | Restricted usage of ESObject type | +| Resource Conversion Errors | Resource to string/number conversion errors | +| Unused Variable Warnings | Declared but never used variables | +| IDataSource Type Errors | LazyForEach requires IDataSource implementation | +| Duplicate Entry Errors | Multiple @Entry decorators in same file | +| Possibly Null Errors | Object possibly null when accessing properties | + +## Quick Reference + +| Error Type | Solution | +|------------|-----------| +| Notification type error | Cast to `number` type | +| Window type error | Use callback pattern for `getLastWindow` | +| AppStorage type error | Use `@StorageLink` with `LocalStorage` or `AppStorage.setAndLink` (avoid `setOrCreate`) | +| Object spread error | Explicitly type objects | +| @StorageLink default value error | Add `= undefined` or specific default value | +| Object literal interface error | Define interface before using object literal | +| Object literal type error | Define interface and use it as return type | +| Function return type error | Add explicit return type annotation | +| Arrow function conversion error | Convert `function` to arrow function `=>` | +| Color property error | Use hex color values instead of non-existent Color properties | +| Interface method signature error | Use property syntax `method: () => {}` instead of method syntax | +| AvoidArea type error | Add `visible: false` property to AvoidArea object | +| Standalone function `this` error | Pass context as parameter: `function foo(context: Context)` | +| TitleButtonRect type error | Use `window.TitleButtonRect` instead of `window.Rect`; only `width` and `height` properties available | +| Catch clause type error | Remove type annotation or use `any`/`unknown` | +| ESObject type error | Use `ESModule` or specific types instead of `ESObject` | +| Resource conversion error | Use Resource directly in UI components or use ResourceManager | +| Unused variable warning | Use console.info/hilog or delete unused variable | +| IDataSource type error | Implement IDataSource interface for LazyForEach | +| Duplicate Entry error | Remove extra @Entry, use @Component for child components | +| Possibly Null error | Use !== null check or optional chaining | + +## Detailed Error Solutions + +### Notification API Type Errors +- [Notification Type Error](./reference/notification_errors.md) +- [Code Example](./assets/NotificationError.ets) + +### Window API Type Errors +- [Window Type Inference Error](./reference/window_type_errors.md) +- [Code Example](./assets/WindowTypeError.ets) + +### AppStorage Type Errors +- [AppStorage Type Error](./reference/appstorage_errors.md) +- [Code Example](./assets/AppStorageError.ets) + +### Object Spread Type Errors +- [Object Spread Type Error](./reference/object_spread_errors.md) +- [Code Example](./assets/ObjectSpreadError.ets) + +### @StorageLink Default Value Errors +- [@StorageLink Default Value Error](./reference/storage_link_default_errors.md) +- [Code Example](./assets/StorageLinkDefaultError.ets) + +### Object Literal Interface Errors +- [Object Literal Interface Error](./reference/object_literal_interface_errors.md) +- [Code Example](./assets/ObjectLiteralInterfaceError.ets) + +### Object Literal Type Errors +- [Object Literal Type Error](./reference/object_literal_type_errors.md) +- [Code Example](./assets/ObjectLiteralTypeError.ets) + +### Function Return Type Errors +- [Function Return Type Error](./reference/function_return_type_errors.md) +- [Code Example](./assets/FunctionReturnTypeError.ets) + +### Arrow Function Conversion Errors +- [Arrow Function Conversion Error](./reference/arrow_function_conversion_errors.md) +- [Code Example](./assets/ArrowFunctionConversionError.ets) + +### Color Property Errors +- [Color Property Error](./reference/color_property_errors.md) +- [Code Example](./assets/ColorPropertyError.ets) + +### Interface Method Signature Errors +- [Interface Method Signature Error](./reference/interface_method_signature_errors.md) +- [Code Example](./assets/InterfaceMethodSignatureError.ets) + +### AvoidArea Type Errors +- [AvoidArea Type Error](./reference/avoid_area_type_errors.md) +- [Code Example](./assets/AvoidAreaTypeError.ets) + +### Standalone Function `this` Errors +- [Standalone Function `this` Error](./reference/standalone_function_errors.md) +- [Code Example](./assets/StandaloneFunctionError.ets) + +### TitleButtonRect Type Errors +- [TitleButtonRect Type Error](./reference/title_button_rect_type_errors.md) +- [Code Example](./assets/TitleButtonRectTypeError.ets) + +### Catch Clause Type Errors +- [Catch Clause Type Error](./reference/catch_clause_type_errors.md) +- [Code Example](./assets/CatchClauseTypeError.ets) + +### ESObject Type Errors +- [ESObject Type Error](./reference/esobject_type_errors.md) +- [Code Example](./assets/ESObjectTypeError.ets) + +### Resource Conversion Errors +- [Resource Conversion Error](./reference/resource_conversion_errors.md) +- [Code Example](./assets/ResourceConversionError.ets) + +### Unused Variable Warnings +- [Unused Variable Warning](./reference/unused_variable_warnings.md) +- [Code Example](./assets/UnusedVariableWarning.ets) + +### IDataSource Type Errors +- [IDataSource Type Error](./reference/idata_source_errors.md) +- [Code Example](./assets/IDataSourceError.ets) + +### Duplicate Entry Errors +- [Duplicate Entry Error](./reference/duplicate_entry_errors.md) +- [Code Example](./assets/DuplicateEntryError.ets) + +### Possibly Null Errors +- [Possibly Null Error](./reference/possibly_null_errors.md) +- [Code Example](./assets/PossiblyNullError.ets) + +### Window Rect/Size Type Errors +- [Window Rect/Size Type Error](./reference/window_rect_size_errors.md) +- [Code Example](./assets/WindowRectSizeError.ets) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/AnyTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/AnyTypeError.ets new file mode 100644 index 0000000000..4d53dfe212 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/AnyTypeError.ets @@ -0,0 +1,269 @@ +/** + * ❌ 错误示例:使用 any 类型 + * + * 错误原因:ArkTS 不允许使用 any 类型 + * 错误信息:Use explicit types instead of "any", "unknown" (arkts-no-any-unknown) + */ +// @Entry +// @Component +// struct AnyTypeError { +// @State data: any = { name: 'John', age: 30 }; +// +// // ❌ 错误:使用 any 类型 +// private processData(input: any): void { +// console.info(input.name); +// console.info(input.age); +// } +// +// // ❌ 错误:使用 any 类型 +// private handleEvent(event: any): void { +// console.info(event.target); +// console.info(event.value); +// } +// +// build() { +// Column() { +// Text('Any Type Error') +// .fontSize(20) +// .margin({ bottom: 20 }) +// +// Button('Process Data') +// .onClick(() => { +// this.processData(this.data); +// }) +// } +// } +// } + +/** + * ✅ 正确示例:使用明确的类型 + */ +@Entry +@Component +struct ExplicitTypeCorrect { + @State data: UserData = { name: 'John', age: 30 }; + + // ✅ 正确:使用明确的类型 + private processData(input: UserData): void { + console.info(input.name); + console.info(input.age.toString()); + } + + // ✅ 正确:使用明确的类型 + private handleEvent(event: ClickEvent): void { + console.info(event.target.toString()); + console.info(event.timestamp.toString()); + } + + build() { + Column() { + Text('Explicit Type Correct') + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Process Data') + .onClick(() => { + this.processData(this.data); + }) + } + } +} + +/** + * ✅ 正确示例:使用接口定义类型 + */ +interface UserData { + name: string; + age: number; + email?: string; +} + +@Entry +@Component +struct InterfaceTypeCorrect { + @State data: UserData = { name: 'John', age: 30 }; + + // ✅ 正确:使用接口定义类型 + private processData(input: UserData): void { + console.info(input.name); + console.info(input.age.toString()); + if (input.email) { + console.info(input.email); + } + } + + build() { + Column() { + Text('Interface Type Correct') + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Process Data') + .onClick(() => { + this.processData(this.data); + }) + } + } +} + +/** + * ✅ 正确示例:使用联合类型 + */ +type StringOrNumber = string | number; + +@Entry +@Component +struct UnionTypeCorrect { + @State value: StringOrNumber = 'Hello'; + + // ✅ 正确:使用联合类型 + private displayValue(value: StringOrNumber): string { + if (typeof value === 'string') { + return value; + } else { + return value.toString(); + } + } + + build() { + Column() { + Text(this.displayValue(this.value)) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Toggle Value') + .onClick(() => { + if (typeof this.value === 'string') { + this.value = 42; + } else { + this.value = 'Hello'; + } + }) + } + } +} + +/** + * ✅ 正确示例:使用泛型 + */ +@Entry +@Component +struct GenericTypeCorrect { + @State items: number[] = [1, 2, 3, 4, 5]; + + // ✅ 正确:使用泛型 + private findItem(array: T[], predicate: (item: T) => boolean): T | undefined { + for (const item of array) { + if (predicate(item)) { + return item; + } + } + return undefined; + } + + build() { + Column() { + Text('Generic Type Correct') + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Find Item') + .onClick(() => { + const found = this.findItem(this.items, (item) => item > 3); + console.info(`Found: ${found}`); + }) + } + } +} + +/** + * ✅ 正确示例:使用 Object 类型 + */ +interface Config { + width: number; + height: number; +} + +interface LogArg { + key: string; +} + +@Entry +@Component +struct ObjectTypeCorrect { + @State config: Config = { width: 100, height: 200 }; + + // ✅ 正确:使用 Object 类型 + private applyConfig(config: Object): void { + console.info(JSON.stringify(config)); + } + + // ✅ 正确:使用 Object[] 作为参数类型 + private logArgs(...args: Object[]): void { + args.forEach(arg => { + console.info(JSON.stringify(arg)); + }); + } + + build() { + Column() { + Text('Object Type Correct') + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Apply Config') + .onClick(() => { + this.applyConfig(this.config); + const logArg: LogArg = { key: 'value' }; + this.logArgs('arg1', 42, logArg); + }) + } + } +} + +/** + * ✅ 正确示例:使用类型守卫 + */ +interface StringData { + type: 'string'; + value: string; +} + +interface NumberData { + type: 'number'; + value: number; +} + +type Data = StringData | NumberData; + +@Entry +@Component +struct TypeGuardCorrect { + @State data: Data = { type: 'string', value: 'Hello' }; + + // ✅ 正确:使用类型守卫 + private processData(data: Data): string { + if (data.type === 'string') { + return data.value; + } else { + return data.value.toString(); + } + } + + build() { + Column() { + Text(this.processData(this.data)) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Toggle Data') + .onClick(() => { + if (this.data.type === 'string') { + this.data = { type: 'number', value: 42 }; + } else { + this.data = { type: 'string', value: 'Hello' }; + } + }) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/AppStorageError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/AppStorageError.ets new file mode 100644 index 0000000000..da89838904 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/AppStorageError.ets @@ -0,0 +1,22 @@ +@Entry +@Component +struct AppStorageError { + @StorageLink('myValue') myValue: number = 0; + + aboutToAppear() { + AppStorage.setOrCreate('myValue', 0); + } + + build() { + Column() { + Text(`Value: ${this.myValue}`) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Increment') + .onClick(() => { + this.myValue += 1; + }) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ArrowFunctionConversionError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ArrowFunctionConversionError.ets new file mode 100644 index 0000000000..091a79a8c1 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ArrowFunctionConversionError.ets @@ -0,0 +1,42 @@ +import { mediaquery } from '@kit.ArkUI'; + +@Entry +@Component +struct ArrowFunctionConversionError { + @State isWideScreen: boolean = false; + private mediaListener: mediaquery.MediaQueryListener | null = null; + + aboutToAppear() { + const mediaQuery = this.getUIContext().getMediaQuery(); + this.mediaListener = mediaQuery.matchMediaSync('(min-width: 600vp)'); + + if (this.mediaListener) { + this.isWideScreen = this.mediaListener.matches; + + this.mediaListener.on('change', (result: mediaquery.MediaQueryResult) => { + this.isWideScreen = result.matches; + }); + } + } + + aboutToDisappear() { + if (this.mediaListener) { + this.mediaListener.off('change'); + } + } + + build() { + Column() { + Text('Function.bind 错误示例') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 16 }) + + Text(`屏幕宽度: ${this.isWideScreen ? '宽屏' : '窄屏'}`) + .fontSize(16) + .fontColor('#333333') + } + .width('100%') + .padding(16) + } +} \ No newline at end of file diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/AvoidAreaTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/AvoidAreaTypeError.ets new file mode 100644 index 0000000000..3f4cb7d7ef --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/AvoidAreaTypeError.ets @@ -0,0 +1,43 @@ +import { window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +@Entry +@Component +struct AvoidAreaTypeError { + @State avoidArea: window.AvoidArea = { + topRect: { left: 0, top: 0, width: 0, height: 0 }, + bottomRect: { left: 0, top: 0, width: 0, height: 0 }, + leftRect: { left: 0, top: 0, width: 0, height: 0 }, + rightRect: { left: 0, top: 0, width: 0, height: 0 }, + visible: false + }; + + aboutToAppear() { + const context = this.getUIContext().getHostContext() as common.UIAbilityContext; + + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + console.error('获取窗口失败:', err); + return; + } + + this.avoidArea = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); + + win.on('avoidAreaChange', (data) => { + if (data.type === window.AvoidAreaType.TYPE_SYSTEM) { + this.avoidArea = data.area; + } + }); + }); + } + + build() { + Column() { + Text(`Top height: ${this.avoidArea.topRect.height}`) + Text(`Bottom height: ${this.avoidArea.bottomRect.height}`) + Text(`Left width: ${this.avoidArea.leftRect.width}`) + Text(`Right width: ${this.avoidArea.rightRect.width}`) + Text(`Visible: ${this.avoidArea.visible}`) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/BreakpointTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/BreakpointTypeError.ets new file mode 100644 index 0000000000..915458afc1 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/BreakpointTypeError.ets @@ -0,0 +1,71 @@ +import { mediaquery } from '@kit.ArkUI'; + +@Entry +@Component +struct BreakpointTypeError { + @State currentBreakpoint: string = 'sm'; + private smListener: mediaquery.MediaQueryListener | null = null; + private mdListener: mediaquery.MediaQueryListener | null = null; + private lgListener: mediaquery.MediaQueryListener | null = null; + + aboutToAppear() { + const mediaQuery = this.getUIContext().getMediaQuery(); + + this.smListener = mediaQuery.matchMediaSync('(0vp<=width<600vp)'); + this.mdListener = mediaQuery.matchMediaSync('(600vp<=width<840vp)'); + this.lgListener = mediaQuery.matchMediaSync('(840vp<=width)'); + + const smCallback = (result: mediaquery.MediaQueryResult): void => { + if (result.matches) { + this.currentBreakpoint = 'sm'; + } + }; + const mdCallback = (result: mediaquery.MediaQueryResult): void => { + if (result.matches) { + this.currentBreakpoint = 'md'; + } + }; + const lgCallback = (result: mediaquery.MediaQueryResult): void => { + if (result.matches) { + this.currentBreakpoint = 'lg'; + } + }; + + if (this.smListener) { + this.smListener.on('change', smCallback); + } + if (this.mdListener) { + this.mdListener.on('change', mdCallback); + } + if (this.lgListener) { + this.lgListener.on('change', lgCallback); + } + } + + aboutToDisappear() { + if (this.smListener) { + this.smListener.off('change'); + } + if (this.mdListener) { + this.mdListener.off('change'); + } + if (this.lgListener) { + this.lgListener.off('change'); + } + } + + build() { + Column() { + Text('断点类型错误示例') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 16 }) + + Text(`当前断点: ${this.currentBreakpoint}`) + .fontSize(16) + .fontColor('#333333') + } + .width('100%') + .padding(16) + } +} \ No newline at end of file diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/CatchClauseTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/CatchClauseTypeError.ets new file mode 100644 index 0000000000..4acd4d4ea9 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/CatchClauseTypeError.ets @@ -0,0 +1,54 @@ +import { camera } from '@kit.CameraKit'; +import { common } from '@kit.AbilityKit'; + +@Entry +@Component +struct CatchClauseTypeError { + private cameraManager: camera.CameraManager | null = null; + + async aboutToAppear() { + await this.initCamera(); + } + + async initCamera() { + try { + const context = this.getUIContext().getHostContext() as common.UIAbilityContext; + this.cameraManager = camera.getCameraManager(context); + console.log('相机管理器初始化成功'); + } catch (error) { + console.error('初始化相机失败:', error); + } + } + + async releaseCamera() { + if (this.cameraManager) { + try { + console.log('相机管理器已释放'); + } catch (error) { + console.error('释放相机失败:', error); + } + } + } + + build() { + Column() { + Text('Catch Clause Type Error Example') + .fontSize(20) + .margin(20) + + Button('初始化相机') + .onClick(() => { + this.initCamera(); + }) + .margin(10) + + Button('释放相机') + .onClick(() => { + this.releaseCamera(); + }) + .margin(10) + } + .width('100%') + .height('100%') + } +} \ No newline at end of file diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ColorConsistencyError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ColorConsistencyError.ets new file mode 100644 index 0000000000..9bb39c5960 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ColorConsistencyError.ets @@ -0,0 +1,88 @@ +@Entry +@Component +struct ColorConsistencyExample { + @State useSystemColor: boolean = false; + + build() { + Column() { + Text('颜色一致性示例') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 20 }) + + Text('使用硬编码颜色(不推荐)') + .fontSize(14) + .fontColor('#666666') + .margin({ bottom: 8 }) + + Column() { + Text('白色背景') + .fontSize(16) + .padding(12) + .margin({ bottom: 8 }) + .width('100%') + .backgroundColor('#FFFFFF') + .borderRadius(8) + + Text('灰色背景') + .fontSize(16) + .padding(12) + .margin({ bottom: 8 }) + .width('100%') + .backgroundColor('#F5F5F5') + .borderRadius(8) + + Text('蓝色背景') + .fontSize(16) + .padding(12) + .width('100%') + .backgroundColor('#007DFF') + .fontColor('#FFFFFF') + .borderRadius(8) + } + .width('100%') + .padding(16) + .backgroundColor('#F0F0F0') + .borderRadius(8) + .margin({ bottom: 20 }) + + Text('使用系统颜色资源(推荐)') + .fontSize(14) + .fontColor('#666666') + .margin({ bottom: 8 }) + + Column() { + Text('系统背景色') + .fontSize(16) + .padding(12) + .margin({ bottom: 8 }) + .width('100%') + .backgroundColor($r('sys.color.ohos_id_color_background')) + .borderRadius(8) + + Text('系统分割线色') + .fontSize(16) + .padding(12) + .margin({ bottom: 8 }) + .width('100%') + .backgroundColor($r('sys.color.ohos_id_color_list_separator')) + .borderRadius(8) + + Text('系统主色调') + .fontSize(16) + .padding(12) + .width('100%') + .backgroundColor($r('sys.color.ohos_id_color_primary')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + .borderRadius(8) + } + .width('100%') + .padding(16) + .backgroundColor($r('sys.color.ohos_id_color_sub_background')) + .borderRadius(8) + } + .width('100%') + .height('100%') + .padding(20) + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ColorPropertyError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ColorPropertyError.ets new file mode 100644 index 0000000000..81e884b6fd --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ColorPropertyError.ets @@ -0,0 +1,39 @@ +@Entry +@Component +struct ColorPropertyError { + @State isDarkMode: boolean = false; + + build() { + Column() { + Text('Color Property Example') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 20 }) + + Text('Primary Color') + .fontSize(16) + .fontColor('#007DFF') + .margin({ bottom: 10 }) + + Text('Secondary Color') + .fontSize(16) + .fontColor('#FF6B00') + .margin({ bottom: 10 }) + + Text('Background Color') + .fontSize(16) + .backgroundColor('#F5F5F5') + .padding(10) + .margin({ bottom: 20 }) + + Button('Toggle Theme') + .onClick(() => { + this.isDarkMode = !this.isDarkMode; + }) + } + .width('100%') + .height('100%') + .backgroundColor(this.isDarkMode ? '#1A1A1A' : '#FFFFFF') + .padding(20) + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ContextTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ContextTypeError.ets new file mode 100644 index 0000000000..2ccdef508e --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ContextTypeError.ets @@ -0,0 +1,105 @@ +import { window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +@Entry +@Component +struct ContextTypeErrorExample { + @State windowWidth: number = 0; + @State windowHeight: number = 0; + + async aboutToAppear() { + // ✅ 正确:使用 getUIContext().getHostContext() 并进行类型检查 + const context = this.getUIContext().getHostContext(); + if (context) { + await this.getWindowSize(context as common.UIAbilityContext); + } + } + + private async getWindowSize(context: common.UIAbilityContext) { + try { + const win = await window.getLastWindow(context); + const properties = win.getWindowProperties(); + this.windowWidth = properties.windowRect.width; + this.windowHeight = properties.windowRect.height; + } catch (err) { + console.error('获取窗口大小失败:', err); + } + } + + build() { + Column() { + Text('Context 类型错误示例') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 20 }) + + Column({ space: 12 }) { + Text('❌ 错误示例 1: 直接使用可能为 undefined 的 Context') + .fontSize(14) + .fontColor('#666666') + + Text('const context = this.getUIContext().getHostContext();') + .fontSize(12) + .fontColor('#FF5722') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#FFF3E0') + .borderRadius(4) + + Text('await window.getLastWindow(context); // 类型错误') + .fontSize(12) + .fontColor('#FF5722') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#FFF3E0') + .borderRadius(4) + + Text('✅ 正确示例 1: 添加 null 检查') + .fontSize(14) + .fontColor('#666666') + + Text('const context = this.getUIContext().getHostContext();') + .fontSize(12) + .fontColor('#4CAF50') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#E8F5E9') + .borderRadius(4) + + Text('if (context) {') + .fontSize(12) + .fontColor('#4CAF50') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#E8F5E9') + .borderRadius(4) + + Text(' await window.getLastWindow(context as common.UIAbilityContext);') + .fontSize(12) + .fontColor('#4CAF50') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#E8F5E9') + .borderRadius(4) + + Text('}') + .fontSize(12) + .fontColor('#4CAF50') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#E8F5E9') + .borderRadius(4) + } + .alignItems(HorizontalAlign.Start) + .margin({ bottom: 20 }) + + Text(`窗口大小: ${this.windowWidth} x ${this.windowHeight}`) + .fontSize(16) + .fontColor('#333333') + } + .width('100%') + .height('100%') + .padding(20) + .alignItems(HorizontalAlign.Center) + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/DecoratorStateError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/DecoratorStateError.ets new file mode 100644 index 0000000000..9e08962245 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/DecoratorStateError.ets @@ -0,0 +1,64 @@ +import { window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +class WindowStateManager { + widthBreakpoint: string = 'sm'; + isFolded: boolean = true; + + updateState(width: number, height: number) { + const area = width * height; + if (area < 600 * 600) { + this.widthBreakpoint = 'sm'; + this.isFolded = true; + } else if (area < 840 * 600) { + this.widthBreakpoint = 'md'; + this.isFolded = false; + } else { + this.widthBreakpoint = 'lg'; + this.isFolded = false; + } + } +} + +@Component +struct DecoratorStateExample { + @State currentBreakpoint: string = 'sm'; + @State isFolded: boolean = true; + + aboutToAppear() { + try { + let context = this.getUIContext().getHostContext() as common.UIAbilityContext; + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + console.error('Failed to get window:', err); + return; + } + const properties = win.getWindowProperties(); + this.updateState(properties.windowRect.width, properties.windowRect.height); + }); + } catch (err) { + console.error('Failed to get window:', err); + } + } + + private updateState(width: number, height: number) { + const area = width * height; + if (area < 600 * 600) { + this.currentBreakpoint = 'sm'; + this.isFolded = true; + } else if (area < 840 * 600) { + this.currentBreakpoint = 'md'; + this.isFolded = false; + } else { + this.currentBreakpoint = 'lg'; + this.isFolded = false; + } + } + + build() { + Column() { + Text(`Breakpoint: ${this.currentBreakpoint}`) + Text(`Folded: ${this.isFolded ? 'Yes' : 'No'}`) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/DisplayListenerTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/DisplayListenerTypeError.ets new file mode 100644 index 0000000000..bdafbfca89 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/DisplayListenerTypeError.ets @@ -0,0 +1,129 @@ +import { display } from '@kit.ArkUI'; +import { hilog } from '@kit.PerformanceAnalysisKit'; + +/** + * ❌ 错误示例:在 Display 对象上调用 on/off 方法 + * + * 错误原因:display.on() 和 display.off() 是 display 模块的方法, + * 不是 Display 对象实例的方法 + */ +@Entry +@Component +struct DisplayListenerTypeError { + @State displayId: number = 0; + private displayListener?: number; + + aboutToAppear() { + try { + const displayClass = display.getDefaultDisplaySync(); + this.displayId = displayClass.id; + + // ❌ 错误:在 Display 对象上调用 on 方法 + // Type 'void' is not assignable to type 'number' + this.displayListener = displayClass.on('change', () => { + hilog.info(0x0000, 'DisplayError', 'Display changed'); + }); + } catch (error) { + hilog.error(0x0000, 'DisplayError', `Failed to register listener: ${JSON.stringify(error)}`); + } + } + + aboutToDisappear() { + // ❌ 错误:在 Display 对象上调用 off 方法 + // Argument of type 'number' is not assignable to parameter of type 'Callback' + if (this.displayListener !== undefined) { + const displayClass = display.getDefaultDisplaySync(); + displayClass.off('change', this.displayListener); + } + } + + build() { + Column() { + Text(`Display ID: ${this.displayId}`) + .fontSize(20) + } + } +} + +/** + * ✅ 正确示例:在 display 模块上调用 on/off 方法 + */ +@Entry +@Component +struct DisplayListenerCorrect { + @State displayId: number = 0; + private displayListener?: (data: number) => void; + + aboutToAppear() { + try { + const displayClass = display.getDefaultDisplaySync(); + this.displayId = displayClass.id; + + // ✅ 正确:在 display 模块上调用 on 方法 + this.displayListener = (data: number) => { + hilog.info(0x0000, 'DisplayCorrect', `Display changed, ID: ${data}`); + }; + display.on('change', this.displayListener); + } catch (error) { + hilog.error(0x0000, 'DisplayCorrect', `Failed to register listener: ${JSON.stringify(error)}`); + } + } + + aboutToDisappear() { + // ✅ 正确:在 display 模块上调用 off 方法 + if (this.displayListener) { + display.off('change', this.displayListener); + this.displayListener = undefined; + } + } + + build() { + Column() { + Text(`Display ID: ${this.displayId}`) + .fontSize(20) + } + } +} + +/** + * ✅ 正确示例:监听 Display 对象的 availableAreaChange + */ +@Entry +@Component +struct DisplayAvailableAreaCorrect { + @State availableArea: string = 'Unknown'; + private areaListener?: (data: display.Rect) => void; + + aboutToAppear() { + try { + const displayClass = display.getDefaultDisplaySync(); + this.availableArea = `Width: ${displayClass.width}, Height: ${displayClass.height}`; + + // ✅ 正确:在 Display 对象上调用 on 方法监听 availableAreaChange + this.areaListener = (data: display.Rect) => { + hilog.info(0x0000, 'DisplayArea', `Available area changed: ${JSON.stringify(data)}`); + this.availableArea = `Width: ${data.width}, Height: ${data.height}`; + }; + displayClass.on('availableAreaChange', this.areaListener); + } catch (error) { + hilog.error(0x0000, 'DisplayArea', `Failed to register listener: ${JSON.stringify(error)}`); + } + } + + aboutToDisappear() { + // ✅ 正确:在 Display 对象上调用 off 方法注销 availableAreaChange + if (this.areaListener) { + const displayClass = display.getDefaultDisplaySync(); + displayClass.off('availableAreaChange', this.areaListener); + this.areaListener = undefined; + } + } + + build() { + Column() { + Text(`Available Area: ${this.availableArea}`) + .fontSize(20) + .margin({ top: 10 }) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/DuplicateEntryError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/DuplicateEntryError.ets new file mode 100644 index 0000000000..7418f1ca69 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/DuplicateEntryError.ets @@ -0,0 +1,35 @@ +@Entry +@Component +struct DuplicateEntryError { + @State message: string = 'Hello World'; + + build() { + Column() { + Text(this.message) + .fontSize(24) + .margin({ bottom: 20 }) + + ChildComponent() + } + .width('100%') + .height('100%') + .padding(20) + } +} + +@Component +struct ChildComponent { + @State count: number = 0; + + build() { + Column() { + Text(`Count: ${this.count}`) + .fontSize(18) + + Button('Increment') + .onClick(() => { + this.count++; + }) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ESObjectTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ESObjectTypeError.ets new file mode 100644 index 0000000000..c578a2204e --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ESObjectTypeError.ets @@ -0,0 +1,137 @@ +import { hilog } from '@kit.PerformanceAnalysisKit'; + +const DOMAIN = 0xFF00; +const TAG = 'ESObjectTypeError'; + +@Entry +@Component +struct ESObjectTypeError { + @State moduleLoaded: boolean = false; + @State moduleName: string = ''; + @State errorMessage: string = ''; + + async loadNFCModule() { + try { + await import('@kit.ConnectivityKit'); + this.moduleLoaded = true; + this.moduleName = 'NFC'; + this.errorMessage = ''; + hilog.info(DOMAIN, TAG, 'NFC module imported successfully'); + } catch (err) { + hilog.error(DOMAIN, TAG, 'Failed to import NFC module: %{public}s', JSON.stringify(err)); + this.moduleLoaded = false; + this.moduleName = 'NFC'; + this.errorMessage = '模块导入失败'; + } + } + + async loadCameraModule() { + try { + await import('@kit.CameraKit'); + this.moduleLoaded = true; + this.moduleName = 'Camera'; + this.errorMessage = ''; + hilog.info(DOMAIN, TAG, 'Camera module imported successfully'); + } catch (err) { + hilog.error(DOMAIN, TAG, 'Failed to import Camera module: %{public}s', JSON.stringify(err)); + this.moduleLoaded = false; + this.moduleName = 'Camera'; + this.errorMessage = '模块导入失败'; + } + } + + build() { + Column() { + Text('ESObject 类型限制错误示例') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ top: 20, bottom: 20 }) + + Text('错误写法(不推荐):') + .fontSize(14) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 10 }) + + Text(`let nfcModule: ESObject = null;`) + .fontSize(12) + .fontColor('#FF0000') + .fontFamily('monospace') + .padding(10) + .backgroundColor('#FFF0F0') + .borderRadius(4) + .margin({ bottom: 20 }) + + Text('正确写法(推荐):') + .fontSize(14) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 10 }) + + Text(`let nfcModule: ESModule | null = null;`) + .fontSize(12) + .fontColor('#00AA00') + .fontFamily('monospace') + .padding(10) + .backgroundColor('#F0FFF0') + .borderRadius(4) + .margin({ bottom: 20 }) + + Row() { + Button('加载 NFC 模块') + .onClick(() => { + this.loadNFCModule(); + }) + .margin({ right: 10 }) + + Button('加载 Camera 模块') + .onClick(() => { + this.loadCameraModule(); + }) + } + .margin({ bottom: 20 }) + + if (this.moduleName) { + Column() { + Text(`模块名称: ${this.moduleName}`) + .fontSize(14) + .margin({ bottom: 5 }) + + if (this.moduleLoaded) { + Text('状态: 加载成功 ✓') + .fontSize(14) + .fontColor('#00AA00') + } else { + Text(`状态: ${this.errorMessage} ✗`) + .fontSize(14) + .fontColor('#FF0000') + } + } + .width('100%') + .padding(15) + .backgroundColor('#F5F5F5') + .borderRadius(8) + } + + Text('说明:') + .fontSize(14) + .fontWeight(FontWeight.Bold) + .margin({ top: 20, bottom: 10 }) + + Text('• ESObject 类型使用受限,不能直接用作变量类型') + .fontSize(12) + .fontColor('#666666') + .margin({ bottom: 5 }) + + Text('• 使用 ESModule 类型或具体类型来声明动态导入的模块') + .fontSize(12) + .fontColor('#666666') + .margin({ bottom: 5 }) + + Text('• 使用 import() 函数进行动态导入') + .fontSize(12) + .fontColor('#666666') + } + .width('100%') + .height('100%') + .padding(20) + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/FontColorPropertyError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/FontColorPropertyError.ets new file mode 100644 index 0000000000..99ab5b57fa --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/FontColorPropertyError.ets @@ -0,0 +1,42 @@ +@Entry +@Component +struct FontColorPropertyError { + @State message: string = '点击按钮测试'; + @State showToast: boolean = false; + + build() { + Column() { + Text('fontColor 属性错误示例') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 16 }) + + Text('正确:Text 组件使用 fontColor') + .fontSize(16) + .fontColor('#333333') + .margin({ bottom: 8 }) + + Text('正确:使用 fontColor 替代 textColor') + .fontSize(16) + .fontColor('#666666') + .margin({ bottom: 8 }) + + Button('点击提示') + .onClick(() => { + this.showToast = true; + setTimeout(() => { + this.showToast = false; + }, 2000); + }) + + if (this.showToast) { + Text(this.message) + .fontSize(14) + .fontColor('#666666') + .margin({ top: 8 }) + } + } + .width('100%') + .padding(16) + } +} \ No newline at end of file diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/FunctionReturnTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/FunctionReturnTypeError.ets new file mode 100644 index 0000000000..62a26aed51 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/FunctionReturnTypeError.ets @@ -0,0 +1,41 @@ +interface WindowInfo { + width: number; + height: number; +} + +@Entry +@Component +struct FunctionReturnTypeError { + @State windowWidth: number = 0; + @State windowHeight: number = 0; + + aboutToAppear() { + this.updateWindowInfo(); + } + + private getWindowInfo(): WindowInfo { + return { + width: 1080, + height: 2340 + }; + } + + private updateWindowInfo(): void { + const info = this.getWindowInfo(); + this.windowWidth = info.width; + this.windowHeight = info.height; + } + + build() { + Column() { + Text(`Window: ${this.windowWidth} x ${this.windowHeight}`) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Refresh') + .onClick(() => { + this.updateWindowInfo(); + }) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/IDataSourceError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/IDataSourceError.ets new file mode 100644 index 0000000000..a5ae9c6999 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/IDataSourceError.ets @@ -0,0 +1,79 @@ +// 错误示例 - LazyForEach 使用 string[] 直接报错 +@Entry +@Component +struct IDataSourceErrorExample { + private data: string[] = ['Item 1', 'Item 2', 'Item 3']; + + build() { + Column() { + List() { + LazyForEach(this.data, (item: string) => { + ListItem() { + Text(item) + } + }, (item: string) => item) + } + .width('100%') + .height('100%') + } + } +} + +// 解决方案 - 实现 IDataSource 接口 +class MyDataSource { + data: string[] = []; + private listeners: DataChangeListener[] = []; + + totalCount(): number { + return this.data.length; + } + + getData(index: number): string { + return this.data[index]; + } + + registerDataChangeListener(listener: DataChangeListener): void { + this.listeners.push(listener); + } + + unregisterDataChangeListener(listener: DataChangeListener): void { + const index = this.listeners.indexOf(listener); + if (index > -1) { + this.listeners.splice(index, 1); + } + } + + pushData(data: string): void { + this.data.push(data); + this.listeners.forEach((listener: DataChangeListener) => { + listener.onDataAdd?.(this.data.length - 1); + }); + } +} + +@Entry +@Component +struct IDataSourceCorrectExample { + private data: MyDataSource = new MyDataSource(); + + aboutToAppear() { + for (let i = 0; i < 100; i++) { + this.data.pushData(`Item ${i}`); + } + } + + build() { + Column() { + List() { + LazyForEach(this.data, (item: string) => { + ListItem() { + Text(item).fontSize(20).padding(10) + } + }, (item: string) => item) + } + .cachedCount(5) + .width('100%') + .height('100%') + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ImplementationNotAllowedError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ImplementationNotAllowedError.ets new file mode 100644 index 0000000000..8c6b7c1f9a --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ImplementationNotAllowedError.ets @@ -0,0 +1,38 @@ +@Entry +@Component +struct ImplementationNotAllowedError { + @State count: number = 0; + + build() { + Column() { + Text('实现不允许错误示例') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 16 }) + + Text(`计数: ${this.count}`) + .fontSize(16) + .fontColor('#333333') + .margin({ bottom: 16 }) + + Button('增加') + .onClick(() => { + this.count++; + }) + .margin({ bottom: 16 }) + + Row() { + Text('Row 内容') + .fontSize(14) + .fontColor('#666666') + } + .width('100%') + .padding(12) + .backgroundColor('#F5F5F5') + .borderRadius(4) + } + .width('100%') + .height('100%') + .padding(16) + } +} \ No newline at end of file diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/InterfaceMethodSignatureError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/InterfaceMethodSignatureError.ets new file mode 100644 index 0000000000..2b22e8d3cb --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/InterfaceMethodSignatureError.ets @@ -0,0 +1,43 @@ +interface WindowUtilDensity { + onDensityUpdate: () => void; + updateWindowInfo: () => void; + destroy: () => void; +} + +export const windowUtilDensityExample: WindowUtilDensity = { + onDensityUpdate: () => { + console.log('Density updated'); + }, + + updateWindowInfo: () => { + console.log('Window info updated'); + }, + + destroy: () => { + console.log('Window util destroyed'); + } +}; + +@Entry +@Component +struct InterfaceMethodSignatureError { + @State windowWidth: number = 0; + @State windowHeight: number = 0; + + aboutToAppear() { + windowUtilDensityExample.updateWindowInfo(); + } + + build() { + Column() { + Text(`Window: ${this.windowWidth} x ${this.windowHeight}`) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Update Info') + .onClick(() => { + windowUtilDensityExample.updateWindowInfo(); + }) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/NotificationError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/NotificationError.ets new file mode 100644 index 0000000000..36544f6881 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/NotificationError.ets @@ -0,0 +1,34 @@ +import { notificationManager } from '@kit.NotificationKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; + +@Entry +@Component +struct NotificationError { + publishNotification() { + let notificationRequest: notificationManager.NotificationRequest = { + id: 1, + content: { + contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT as number, + normal: { + title: 'Test Notification', + text: 'This is a test notification', + additionalText: 'Additional text' + } + } + }; + + notificationManager.publish(notificationRequest).then(() => { + hilog.info(0x0000, 'testTag', 'Publish notification success'); + }).catch((err: BusinessError) => { + hilog.error(0x0000, 'testTag', 'Publish notification failed: %{public}s', JSON.stringify(err)); + }); + } + + build() { + Column() { + Button('Publish Notification') + .onClick(() => this.publishNotification()) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ObjectLiteralInterfaceError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ObjectLiteralInterfaceError.ets new file mode 100644 index 0000000000..8c4dd113a3 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ObjectLiteralInterfaceError.ets @@ -0,0 +1,49 @@ +interface Article { + title: string; + desc: string; + image: Resource; +} + +@Entry +@Component +struct ObjectLiteralInterfaceError { + private articles: Article[] = [ + { title: '文章1', desc: '这是文章1的描述', image: $r('app.media.article1') }, + { title: '文章2', desc: '这是文章2的描述', image: $r('app.media.article2') }, + { title: '文章3', desc: '这是文章3的描述', image: $r('app.media.article3') } + ]; + + build() { + Scroll() { + Column() { + ForEach(this.articles, (article: Article, index: number) => { + Column() { + Image(article.image) + .width('100%') + .height(200) + .objectFit(ImageFit.Cover) + .borderRadius(8) + + Text(article.title) + .fontSize('20fp') + .fontWeight(FontWeight.Bold) + .margin({ top: 8 }) + + Text(article.desc) + .fontSize('14fp') + .fontColor('#666666') + .margin({ top: 4 }) + } + .padding(16) + .backgroundColor('#FFFFFF') + .borderRadius(8) + .margin({ bottom: 16 }) + }) + } + .padding({ left: 16, right: 16, top: 16, bottom: 16 }) + } + .width('100%') + .height('100%') + .backgroundColor('#F5F5F5') + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ObjectLiteralTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ObjectLiteralTypeError.ets new file mode 100644 index 0000000000..64007c5d3f --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ObjectLiteralTypeError.ets @@ -0,0 +1,54 @@ +import { common } from '@kit.AbilityKit'; + +interface WindowSize { + width: number; + height: number; +} + +@Entry +@Component +struct ObjectLiteralTypeError { + @State windowSize: WindowSize = { width: 0, height: 0 }; + + aboutToAppear() { + this.windowSize = this.getWindowSize(); + } + + private getWindowSize(): WindowSize { + const context = this.getUIContext().getHostContext() as common.UIAbilityContext; + const windowStage = context.windowStage; + if (!windowStage) { + return { width: 0, height: 0 }; + } + const windowClass = windowStage.getMainWindowSync(); + const windowProperties = windowClass.getWindowProperties(); + return { + width: windowProperties.windowRect.width, + height: windowProperties.windowRect.height + }; + } + + build() { + Column() { + Text('Object Literal Type Error Example') + .fontSize(20) + .margin(20) + + Text(`窗口宽度: ${this.windowSize.width}`) + .fontSize(16) + .margin(10) + + Text(`窗口高度: ${this.windowSize.height}`) + .fontSize(16) + .margin(10) + + Button('刷新窗口尺寸') + .onClick(() => { + this.windowSize = this.getWindowSize(); + }) + .margin(20) + } + .width('100%') + .height('100%') + } +} \ No newline at end of file diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ObjectSpreadError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ObjectSpreadError.ets new file mode 100644 index 0000000000..2c46fb97b0 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ObjectSpreadError.ets @@ -0,0 +1,29 @@ +interface Config { + width: number; + height: number; + color?: string; +} + +@Entry +@Component +struct ObjectSpreadError { + @State config: Config = { width: 100, height: 100 }; + + build() { + Column() { + Text(`Width: ${this.config.width}, Height: ${this.config.height}`) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Update Config') + .onClick(() => { + const newConfig: Config = { + width: this.config.width, + height: this.config.height, + color: 'red' + }; + this.config = newConfig; + }) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/PossiblyNullError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/PossiblyNullError.ets new file mode 100644 index 0000000000..f6ab508a77 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/PossiblyNullError.ets @@ -0,0 +1,32 @@ +import { display } from '@kit.ArkUI'; + +@Entry +@Component +struct PossiblyNullError { + private myDisplay: display.Display | null = null; + + aboutToAppear() { + this.myDisplay = display.getDefaultDisplaySync(); + } + + build() { + Column() { + if (this.myDisplay !== null) { + Text(`Width: ${this.myDisplay.width}`) + .fontSize(24) + } else { + Text('Display not available') + .fontSize(24) + } + + Button('Check Display') + .onClick(() => { + if (this.myDisplay !== null) { + console.log(`Display: ${this.myDisplay.width}x${this.myDisplay.height}`); + } + }) + } + .width('100%') + .height('100%') + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ResourceConversionError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ResourceConversionError.ets new file mode 100644 index 0000000000..7d8ffff5f7 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/ResourceConversionError.ets @@ -0,0 +1,159 @@ +import { hilog } from '@kit.PerformanceAnalysisKit'; + +const DOMAIN = 0xFF00; +const TAG = 'ResourceConversionError'; + +@Entry +@Component +struct ResourceConversionError { + @State displayText: string = ''; + @State fontSize: number = 16; + @State paddingValue: number = 10; + + loadStringResource() { + try { + const context = this.getUIContext().getHostContext(); + if (!context) { + hilog.error(DOMAIN, TAG, 'Context is null or undefined'); + this.displayText = '加载失败'; + return; + } + const manager = context.resourceManager; + const resourceId = $r('app.string.hello').id; + this.displayText = manager.getString(resourceId); + hilog.info(DOMAIN, TAG, 'String resource loaded: %{public}s', this.displayText); + } catch (err) { + hilog.error(DOMAIN, TAG, 'Failed to load string resource: %{public}s', + JSON.stringify(err)); + this.displayText = '加载失败'; + } + } + + loadNumberResource() { + try { + const context = this.getUIContext().getHostContext(); + if (!context) { + hilog.error(DOMAIN, TAG, 'Context is null or undefined'); + this.fontSize = 16; + return; + } + const manager = context.resourceManager; + const resourceId = $r('app.float.title_font_size').id; + this.fontSize = manager.getNumber(resourceId); + hilog.info(DOMAIN, TAG, 'Number resource loaded: %{public}d', this.fontSize); + } catch (err) { + hilog.error(DOMAIN, TAG, 'Failed to load number resource: %{public}s', + JSON.stringify(err)); + this.fontSize = 16; + } + } + + build() { + Column() { + Text('Resource 类型转换错误示例') + .fontSize($r('app.float.title_font_size')) + .fontWeight(FontWeight.Bold) + .margin({ top: 20, bottom: 20 }) + + Text('错误写法(不推荐):') + .fontSize(14) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 10 }) + + Text(`const message: string = $r('app.string.hello');`) + .fontSize(12) + .fontColor('#FF0000') + .fontFamily('monospace') + .padding(10) + .backgroundColor('#FFF0F0') + .borderRadius(4) + .margin({ bottom: 20 }) + + Text('正确写法(推荐):') + .fontSize(14) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 10 }) + + Text('直接在 UI 组件中使用 Resource 类型') + .fontSize(12) + .fontColor('#666666') + .margin({ bottom: 10 }) + + Text($r('app.string.hello')) + .fontSize($r('app.float.content_font_size')) + .width($r('app.float.layout_width')) + .padding($r('app.float.padding')) + .backgroundColor($r('app.color.background_color')) + .borderRadius($r('app.float.border_radius')) + .margin({ bottom: 20 }) + + Text('方案1:直接在 UI 组件中使用(推荐)') + .fontSize(14) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 10 }) + + Text('Resource 类型可以直接作为属性值传递给 UI 组件') + .fontSize(12) + .fontColor('#666666') + .margin({ bottom: 10 }) + + Text('方案2:使用 ResourceManager 获取实际值') + .fontSize(14) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 10 }) + + Row() { + Button('加载字符串资源') + .onClick(() => { + this.loadStringResource(); + }) + .margin({ right: 10 }) + + Button('加载数值资源') + .onClick(() => { + this.loadNumberResource(); + }) + } + .margin({ bottom: 20 }) + + if (this.displayText) { + Text(`加载的字符串: ${this.displayText}`) + .fontSize(14) + .margin(20) + } + + if (this.fontSize !== 16) { + Text(`加载的字体大小: ${this.fontSize}`) + .fontSize(14) + .margin(20) + } + + Text('说明:') + .fontSize(14) + .fontWeight(FontWeight.Bold) + .margin({ top: 20, bottom: 10 }) + + Text('• Resource 类型不能直接转换为 string 或 number') + .fontSize(12) + .fontColor('#666666') + .margin({ bottom: 5 }) + + Text('• 直接在 UI 组件中使用 Resource 类型') + .fontSize(12) + .fontColor('#666666') + .margin({ bottom: 5 }) + + Text('• 使用 ResourceManager 获取实际值') + .fontSize(12) + .fontColor('#666666') + .margin({ bottom: 5 }) + + Text('• Resource 支持多语言和主题适配') + .fontSize(12) + .fontColor('#666666') + } + .width('100%') + .height('100%') + .padding(20) + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/StandaloneFunctionContext.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/StandaloneFunctionContext.ets new file mode 100644 index 0000000000..48683ad4f8 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/StandaloneFunctionContext.ets @@ -0,0 +1,116 @@ +import { window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +async function getAvoidArea(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + const avoidArea = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); + resolve(avoidArea); + }); + }); +} + +async function setImmersiveWindow(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + win.setWindowLayoutFullScreen(true).then(() => { + resolve(); + }).catch((error: Error) => { + reject(new Error(error.message)); + }); + }); + }); +} + +async function getTitleButtonRect(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + const titleButtonRect = win.getTitleButtonRect(); + resolve(titleButtonRect); + }); + }); +} + +async function getWindowProperties(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + const properties = win.getWindowProperties(); + resolve(properties); + }); + }); +} + +@Entry +@Component +struct StandaloneFunctionExample { + @State avoidAreaHeight: number = 0; + @State isImmersive: boolean = false; + @State titleBarHeight: number = 0; + @State windowWidth: number = 0; + @State windowHeight: number = 0; + + async aboutToAppear() { + const context = this.getUIContext().getHostContext() as common.UIAbilityContext; + + try { + const avoidArea = await getAvoidArea(context); + this.avoidAreaHeight = avoidArea.topRect.height; + } catch (err) { + console.error('获取避让区域失败:', err instanceof Error ? err.message : String(err)); + } + + try { + await setImmersiveWindow(context); + this.isImmersive = true; + } catch (err) { + console.error('设置沉浸式窗口失败:', err instanceof Error ? err.message : String(err)); + } + + try { + const titleButtonRect = await getTitleButtonRect(context); + this.titleBarHeight = titleButtonRect.height; + } catch (err) { + console.error('获取标题栏按钮区域失败:', err instanceof Error ? err.message : String(err)); + } + + try { + const properties = await getWindowProperties(context); + this.windowWidth = properties.windowRect.width; + this.windowHeight = properties.windowRect.height; + } catch (err) { + console.error('获取窗口属性失败:', err instanceof Error ? err.message : String(err)); + } + } + + build() { + Column() { + Text('独立函数上下文传递示例') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 20 }) + + Text(`避让区域高度: ${this.avoidAreaHeight}`) + Text(`沉浸式状态: ${this.isImmersive}`) + Text(`标题栏高度: ${this.titleBarHeight}`) + Text(`窗口宽度: ${this.windowWidth}`) + Text(`窗口高度: ${this.windowHeight}`) + } + .padding(20) + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/StandaloneFunctionError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/StandaloneFunctionError.ets new file mode 100644 index 0000000000..0224523fe9 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/StandaloneFunctionError.ets @@ -0,0 +1,85 @@ +import { window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +async function getAvoidArea(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + const avoidArea = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); + resolve(avoidArea); + }); + }); +} + +async function setImmersiveWindow(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + win.setWindowLayoutFullScreen(true).then(() => { + resolve(); + }).catch((error: Error) => { + reject(new Error(error.message)); + }); + }); + }); +} + +async function getTitleButtonRect(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + const titleButtonRect = win.getTitleButtonRect(); + resolve(titleButtonRect); + }); + }); +} + +@Entry +@Component +struct StandaloneFunctionError { + @State avoidAreaHeight: number = 0; + @State isImmersive: boolean = false; + @State titleBarHeight: number = 0; + + async aboutToAppear() { + const context = this.getUIContext().getHostContext() as common.UIAbilityContext; + + try { + const avoidArea = await getAvoidArea(context); + this.avoidAreaHeight = avoidArea.topRect.height; + } catch (err) { + console.error('获取避让区域失败:', err instanceof Error ? err.message : String(err)); + } + + try { + await setImmersiveWindow(context); + this.isImmersive = true; + } catch (err) { + console.error('设置沉浸式窗口失败:', err instanceof Error ? err.message : String(err)); + } + + try { + const titleButtonRect = await getTitleButtonRect(context); + this.titleBarHeight = titleButtonRect.height; + } catch (err) { + console.error('获取标题栏按钮区域失败:', err instanceof Error ? err.message : String(err)); + } + } + + build() { + Column() { + Text(`避让区域高度: ${this.avoidAreaHeight}`) + Text(`沉浸式状态: ${this.isImmersive}`) + Text(`标题栏高度: ${this.titleBarHeight}`) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/StorageLinkDefaultError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/StorageLinkDefaultError.ets new file mode 100644 index 0000000000..8aa9112600 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/StorageLinkDefaultError.ets @@ -0,0 +1,41 @@ +import { window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +@Entry +@Component +struct StorageLinkDefaultError { + @StorageLink('windowUtil') windowUtil?: WindowUtil = undefined; + + aboutToAppear() { + AppStorage.setOrCreate('windowUtil', { + width: 0, + height: 0, + density: 1.0, + updateWindowInfo: () => {}, + destroy: () => {} + }); + } + + build() { + Column() { + Text(`Window Util: ${this.windowUtil?.width || 0}`) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Update Window') + .onClick(() => { + if (this.windowUtil) { + this.windowUtil.width = 100; + } + }) + } + } +} + +interface WindowUtil { + width: number; + height: number; + density: number; + updateWindowInfo: () => void; + destroy: () => void; +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/TitleButtonRectTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/TitleButtonRectTypeError.ets new file mode 100644 index 0000000000..e029549a06 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/TitleButtonRectTypeError.ets @@ -0,0 +1,86 @@ +import { window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +// ❌ 错误示例:返回类型声明为 window.Rect +// async function getTitleButtonRect(context: common.UIAbilityContext): Promise { +// return new Promise((resolve, reject) => { +// window.getLastWindow(context, (err, win) => { +// if (err.code !== 0) { +// reject(new Error(err.message)); +// return; +// } +// const titleButtonRect = win.getTitleButtonRect(); +// resolve(titleButtonRect); // 类型错误:TitleButtonRect 不能赋值给 Rect +// }); +// }); +// } + +// ✅ 正确示例:返回类型声明为 window.TitleButtonRect +async function getTitleButtonRect(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + const titleButtonRect = win.getTitleButtonRect(); + resolve(titleButtonRect); + }); + }); +} + +// ✅ 如果需要 Rect 类型,进行类型转换 +// 注意:TitleButtonRect 类型可能不包含 left 和 top 属性 +// 如果需要完整的 Rect 信息,需要从其他 API 获取 +async function getTitleButtonRectAsRect(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + const titleButtonRect = win.getTitleButtonRect(); + // 创建新的 Rect 对象 + // TitleButtonRect 只包含 width 和 height 属性 + resolve({ + left: 0, + top: 0, + width: titleButtonRect.width, + height: titleButtonRect.height + }); + }); + }); +} + +@Entry +@Component +struct TitleButtonRectTypeError { + @State titleBarHeight: number = 0; + @State titleBarWidth: number = 0; + + async aboutToAppear() { + const context = this.getUIContext().getHostContext() as common.UIAbilityContext; + try { + const titleButtonRect = await getTitleButtonRect(context); + // TitleButtonRect 只包含 width 和 height 属性 + this.titleBarHeight = titleButtonRect.height; + this.titleBarWidth = titleButtonRect.width; + } catch (err) { + console.error('获取标题栏按钮区域失败:', err instanceof Error ? err.message : String(err)); + } + } + + build() { + Column() { + Text('TitleButtonRect 类型错误示例') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 20 }) + + Text(`标题栏高度: ${this.titleBarHeight}`) + .margin({ bottom: 8 }) + Text(`标题栏宽度: ${this.titleBarWidth}`) + } + .padding(20) + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/UnusedVariableWarning.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/UnusedVariableWarning.ets new file mode 100644 index 0000000000..5f4d6a5702 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/UnusedVariableWarning.ets @@ -0,0 +1,102 @@ +@Entry +@Component +struct UnusedVariableWarningExample { + @State scrollOffset: number = 0; + private scroller: Scroller = new Scroller(); + + build() { + Column() { + Text('未使用变量警告示例') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ bottom: 20 }) + + Column({ space: 12 }) { + Text('❌ 错误示例: scrollState 未使用') + .fontSize(14) + .fontColor('#666666') + + Text('.onDidScroll((scrollOffset: number, scrollState: ScrollState) => {') + .fontSize(12) + .fontColor('#FF5722') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#FFF3E0') + .borderRadius(4) + + Text(' this.scrollOffset = scrollOffset;') + .fontSize(12) + .fontColor('#FF5722') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#FFF3E0') + .borderRadius(4) + + Text('})') + .fontSize(12) + .fontColor('#FF5722') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#FFF3E0') + .borderRadius(4) + + Text('✅ 正确示例 1: 使用下划线前缀标记未使用参数') + .fontSize(14) + .fontColor('#666666') + + Text('.onDidScroll((scrollOffset: number, _scrollState: ScrollState) => {') + .fontSize(12) + .fontColor('#4CAF50') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#E8F5E9') + .borderRadius(4) + + Text(' this.scrollOffset = scrollOffset;') + .fontSize(12) + .fontColor('#4CAF50') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#E8F5E9') + .borderRadius(4) + + Text('})') + .fontSize(12) + .fontColor('#4CAF50') + .fontFamily('monospace') + .padding(8) + .backgroundColor('#E8F5E9') + .borderRadius(4) + } + .alignItems(HorizontalAlign.Start) + .margin({ bottom: 20 }) + + Text('实际使用示例:') + .fontSize(14) + .fontColor('#666666') + .margin({ bottom: 8 }) + + Scroll(this.scroller) { + Column() { + ForEach(Array.from({ length: 50 }), (_: Object, index: number) => { + Text(`Item ${index + 1}`) + .fontSize(16) + .padding(12) + .margin({ bottom: 8 }) + .backgroundColor('#F5F5F5') + .borderRadius(4) + }, (_: Object, index: number) => `${index}`) + } + .width('100%') + } + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Auto) + .onDidScroll((scrollOffset: number, _scrollState: ScrollState) => { + this.scrollOffset = scrollOffset; + }) + } + .width('100%') + .height('100%') + .padding(20) + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/UtilityTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/UtilityTypeError.ets new file mode 100644 index 0000000000..575202debc --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/UtilityTypeError.ets @@ -0,0 +1,176 @@ +/** + * ❌ 错误示例:使用 Parameters 工具类型 + * + * 错误原因:ArkTS 不支持 TypeScript 的 Parameters 工具类型 + * 错误信息:Some of utility types are not supported (arkts-no-utility-types) + */ +@Entry +@Component +struct UtilityTypeError { + @State count: number = 0; + + // ❌ 错误:使用 Parameters 工具类型 + private handleClick = (event: ClickEvent) => { + this.count++; + }; + + // ❌ 错误:使用 Parameters 工具类型 + private debounce void>(func: T, delay: number): T { + return ((...args: Parameters) => { + setTimeout(() => { + func(...args); + }, delay); + }) as T; + } + + build() { + Column() { + Text(`Count: ${this.count}`) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Click Me') + .onClick(this.handleClick) + } + } +} + +/** + * ✅ 正确示例:使用 Object[] 替代 Parameters 工具类型 + */ +@Entry +@Component +struct UtilityTypeCorrect { + @State count: number = 0; + + private handleClick = (event: ClickEvent) => { + this.count++; + }; + + // ✅ 正确:使用 Object[] 替代 Parameters + private debounce(func: Function, delay: number): Function { + return (...args: Object[]): void => { + setTimeout(() => { + func(...args); + }, delay); + }; + } + + build() { + Column() { + Text(`Count: ${this.count}`) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Click Me') + .onClick(this.handleClick) + } + } +} + +/** + * ✅ 正确示例:使用明确的类型定义 + */ +@Entry +@Component +struct UtilityTypeExplicit { + @State message: string = 'Hello'; + + // ✅ 正确:使用明确的类型定义 + private handleClick = (event: ClickEvent): void => { + this.message = 'Clicked!'; + }; + + // ✅ 正确:使用明确的类型定义 + private handleInput = (value: string): void => { + this.message = value; + }; + + // ✅ 正确:使用明确的类型定义 + private debounce(func: (event: ClickEvent) => void, delay: number): (event: ClickEvent) => void { + let timeoutId: number = -1; + return (event: ClickEvent): void => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + func(event); + }, delay); + }; + } + + build() { + Column() { + Text(this.message) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Click Me') + .onClick(this.debounce(this.handleClick, 300)) + } + } +} + +/** + * ✅ 正确示例:使用类型别名定义函数类型 + */ +type EventHandler = (event: ClickEvent) => void; +type InputHandler = (value: string) => void; + +@Entry +@Component +struct UtilityTypeInterface { + @State message: string = 'Hello'; + + // ✅ 正确:使用类型别名定义函数类型 + private handleClick: EventHandler = (event: ClickEvent): void => { + this.message = 'Clicked!'; + }; + + // ✅ 正确:使用类型别名定义函数类型 + private handleInput: InputHandler = (value: string): void => { + this.message = value; + }; + + build() { + Column() { + Text(this.message) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Click Me') + .onClick(this.handleClick) + } + } +} + +/** + * ✅ 正确示例:使用类型别名 + */ +type ClickHandler = (event: ClickEvent) => void; +type ValueHandler = (value: string) => void; + +@Entry +@Component +struct UtilityTypeAlias { + @State message: string = 'Hello'; + + // ✅ 正确:使用类型别名 + private handleClick: ClickHandler = (event: ClickEvent): void => { + this.message = 'Clicked!'; + }; + + // ✅ 正确:使用类型别名 + private handleInput: ValueHandler = (value: string): void => { + this.message = value; + }; + + build() { + Column() { + Text(this.message) + .fontSize(20) + .margin({ bottom: 20 }) + + Button('Click Me') + .onClick(this.handleClick) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/WindowRectSizeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/WindowRectSizeError.ets new file mode 100644 index 0000000000..fac9672c01 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/WindowRectSizeError.ets @@ -0,0 +1,65 @@ +// window.Rect 和 window.Size 类型使用示例 +import { window } from '@kit.ArkUI'; + +@Entry +@Component +struct WindowRectSizeExample { + @State windowRect: window.Rect = { left: 0, top: 0, width: 0, height: 0 }; + @State windowSize: window.Size = { width: 0, height: 0 }; + @State breakpoint: string = 'sm'; + + aboutToAppear() { + try { + window.getLastWindow(this.getUIContext().getHostContext(), (err, win) => { + if (err.code !== 0) { + console.error('Failed to get window:', err); + return; + } + const properties = win.getWindowProperties(); + this.windowRect = properties.windowRect; + this.windowSize = { width: properties.windowRect.width, height: properties.windowRect.height }; + this.breakpoint = this.calculateBreakpoint(properties.windowRect.width); + + win.on('windowSizeChange', (size: window.Size) => { + this.windowSize = size; + this.windowRect = { + left: this.windowRect.left, + top: this.windowRect.top, + width: size.width, + height: size.height + }; + this.breakpoint = this.calculateBreakpoint(size.width); + }); + }); + } catch (e) { + console.error('Error:', e); + } + } + + private calculateBreakpoint(width: number): string { + if (width < 600) return 'sm'; + if (width < 840) return 'md'; + if (width < 1440) return 'lg'; + return 'xl'; + } + + build() { + Column({ space: 20 }) { + Text('窗口信息') + .fontSize(24) + .fontWeight(FontWeight.Bold) + + Text(`窗口位置: x=${this.windowRect.left}, y=${this.windowRect.top}`) + .fontSize(16) + + Text(`窗口尺寸: w=${this.windowRect.width}, h=${this.windowRect.height}`) + .fontSize(16) + + Text(`当前断点: ${this.breakpoint}`) + .fontSize(16) + } + .width('100%') + .height('100%') + .padding(20) + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/WindowTypeError.ets b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/WindowTypeError.ets new file mode 100644 index 0000000000..66802c3784 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/assets/WindowTypeError.ets @@ -0,0 +1,37 @@ +import { window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +@Entry +@Component +struct WindowTypeError { + @State windowWidth: number = 0; + + aboutToAppear() { + try { + const context = this.getUIContext().getHostContext() as common.UIAbilityContext; + + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + console.error('Failed to get window:', err); + return; + } + + const properties = win.getWindowProperties(); + this.windowWidth = properties.windowRect.width; + + win.on('windowSizeChange', (size: window.Size) => { + this.windowWidth = size.width; + }); + }); + } catch (err) { + console.error('Failed to get context:', err); + } + } + + build() { + Column() { + Text(`Window Width: ${this.windowWidth}`) + .fontSize(20) + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/any_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/any_type_errors.md new file mode 100644 index 0000000000..26fc051759 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/any_type_errors.md @@ -0,0 +1,433 @@ +# Any Type Errors + +## Error: Use explicit types instead of "any", "unknown" + +### Error Message +``` +Use explicit types instead of "any", "unknown" (arkts-no-any-unknown) +``` + +### Cause +ArkTS requires explicit type definitions and does not allow the use of `any` or `unknown` types. This is part of ArkTS's stricter type system designed to improve type safety and reduce runtime errors. + +### Solution +Use explicit type definitions, interfaces, type aliases, union types, or generics instead of `any` or `unknown`. + +### Key Points +- ArkTS does not allow `any` or `unknown` types +- Use explicit types for all variables and parameters +- Define interfaces for complex object types +- Use union types for multiple possible types +- Use generics for reusable type-safe code + +### ❌ Wrong Usage +```typescript +// ❌ Wrong: Using any type +let data: any = { name: 'John', age: 30 }; + +// ❌ Wrong: Using any in function parameters +function processData(input: any): void { + console.info(input.name); +} + +// ❌ Wrong: Using any in event handlers +function handleEvent(event: any): void { + console.info(event.target); +} + +// ❌ Wrong: Using unknown type +let value: unknown = 'Hello'; +``` + +### ✅ Correct Usage +```typescript +// ✅ Correct: Using explicit type +let data: { name: string; age: number } = { name: 'John', age: 30 }; + +// ✅ Correct: Using interface +interface UserData { + name: string; + age: number; +} + +function processData(input: UserData): void { + console.info(input.name); +} + +// ✅ Correct: Using explicit event type +function handleEvent(event: ClickEvent): void { + console.info(event.target.toString()); +} +``` + +### Interface Definitions + +#### Simple Interface +```typescript +interface UserData { + name: string; + age: number; + email?: string; +} + +@Component +struct UserComponent { + @State user: UserData = { name: 'John', age: 30 }; + + private displayUser(data: UserData): string { + return `${data.name}, ${data.age}`; + } + + build() { + Text(this.displayUser(this.user)) + } +} +``` + +#### Nested Interface +```typescript +interface Address { + street: string; + city: string; + zipCode: string; +} + +interface User { + id: number; + name: string; + address: Address; +} + +@Component +struct AddressComponent { + @State user: User = { + id: 1, + name: 'John', + address: { street: '123 Main St', city: 'New York', zipCode: '10001' } + }; + + private displayAddress(user: User): string { + return `${user.address.street}, ${user.address.city}`; + } + + build() { + Text(this.displayAddress(this.user)) + } +} +``` + +### Union Types + +#### Simple Union +```typescript +type StringOrNumber = string | number; + +@Component +struct UnionExample { + @State value: StringOrNumber = 'Hello'; + + private displayValue(value: StringOrNumber): string { + if (typeof value === 'string') { + return value; + } else { + return value.toString(); + } + } + + build() { + Text(this.displayValue(this.value)) + } +} +``` + +#### Complex Union +```typescript +type EventData = ClickEvent | TouchEvent | ScrollEvent; + +@Component +struct EventExample { + private handleEvent(event: EventData): void { + if (event instanceof ClickEvent) { + console.info('Click event'); + } else if (event instanceof TouchEvent) { + console.info('Touch event'); + } else { + console.info('Scroll event'); + } + } + + build() { + Column() { + Button('Click') + .onClick((event: ClickEvent) => this.handleEvent(event)) + } + } +} +``` + +### Generics + +#### Simple Generic +```typescript +@Component +struct GenericExample { + @State items: number[] = [1, 2, 3, 4, 5]; + + private findItem(array: T[], predicate: (item: T) => boolean): T | undefined { + for (const item of array) { + if (predicate(item)) { + return item; + } + } + return undefined; + } + + build() { + Column() { + Button('Find Item') + .onClick(() => { + const found = this.findItem(this.items, (item) => item > 3); + console.info(`Found: ${found}`); + }) + } + } +} +``` + +#### Generic Class +```typescript +class Storage { + private data: T[] = []; + + add(item: T): void { + this.data.push(item); + } + + get(index: number): T | undefined { + return this.data[index]; + } + + find(predicate: (item: T) => boolean): T | undefined { + return this.data.find(predicate); + } +} + +@Component +struct StorageExample { + private storage: Storage = new Storage(); + + aboutToAppear() { + this.storage.add(1); + this.storage.add(2); + this.storage.add(3); + } + + build() { + Column() { + Button('Get Item') + .onClick(() => { + const item = this.storage.get(0); + console.info(`Item: ${item}`); + }) + } + } +} +``` + +### Type Guards + +#### Discriminated Union +```typescript +interface StringData { + type: 'string'; + value: string; +} + +interface NumberData { + type: 'number'; + value: number; +} + +type Data = StringData | NumberData; + +@Component +struct TypeGuardExample { + @State data: Data = { type: 'string', value: 'Hello' }; + + private processData(data: Data): string { + if (data.type === 'string') { + return data.value; + } else { + return data.value.toString(); + } + } + + build() { + Text(this.processData(this.data)) + } +} +``` + +#### Typeof Guard +```typescript +type Value = string | number | boolean; + +@Component +struct TypeofExample { + @State value: Value = 'Hello'; + + private displayValue(value: Value): string { + if (typeof value === 'string') { + return `String: ${value}`; + } else if (typeof value === 'number') { + return `Number: ${value}`; + } else { + return `Boolean: ${value}`; + } + } + + build() { + Text(this.displayValue(this.value)) + } +} +``` + +### Object Type + +#### Using Object +```typescript +@Component +struct ObjectExample { + @State config: Object = { width: 100, height: 200 }; + + private applyConfig(config: Object): void { + console.info(JSON.stringify(config)); + } + + private logArgs(...args: Object[]): void { + args.forEach(arg => { + console.info(JSON.stringify(arg)); + }); + } + + build() { + Column() { + Button('Apply Config') + .onClick(() => { + this.applyConfig(this.config); + this.logArgs('arg1', 42, { key: 'value' }); + }) + } + } +} +``` + +### Type Aliases + +#### Simple Alias +```typescript +type UserId = number; +type UserName = string; +type UserEmail = string; + +interface User { + id: UserId; + name: UserName; + email: UserEmail; +} + +@Component +struct AliasExample { + @State user: User = { id: 1, name: 'John', email: 'john@example.com' }; + + build() { + Text(`${this.user.name} (${this.user.id})`) + } +} +``` + +#### Function Alias +```typescript +type EventHandler = (event: ClickEvent) => void; +type ValueHandler = (value: string) => void; + +@Component +struct FunctionAliasExample { + private onClick: EventHandler = (event: ClickEvent): void => { + console.info('Clicked'); + }; + + private onInput: ValueHandler = (value: string): void => { + console.info(`Input: ${value}`); + }; + + build() { + Column() { + Button('Click') + .onClick(this.onClick) + } + } +} +``` + +### Common Patterns + +#### Event Handlers +```typescript +type ClickHandler = (event: ClickEvent) => void; +type TouchHandler = (event: TouchEvent) => void; +type ScrollHandler = (event: ScrollEvent) => void; + +@Component +struct EventHandlers { + private onClick: ClickHandler = (event: ClickEvent): void => { + console.info('Clicked'); + }; + + private onTouch: TouchHandler = (event: TouchEvent): void => { + console.info('Touched'); + }; + + build() { + Column() { + Button('Click') + .onClick(this.onClick) + } + .onTouch(this.onTouch) + } +} +``` + +#### Data Processing +```typescript +interface DataProcessor { + process(data: T): R; +} + +class StringProcessor implements DataProcessor { + process(data: string): number { + return data.length; + } +} + +@Component +struct ProcessorExample { + private processor: DataProcessor = new StringProcessor(); + + build() { + Text(`Length: ${this.processor.process('Hello')}`) + } +} +``` + +### Best Practices +1. **Use explicit types**: Always define types explicitly +2. **Define interfaces**: Use interfaces for complex object types +3. **Use union types**: Use union types for multiple possible types +4. **Use generics**: Use generics for reusable type-safe code +5. **Use type guards**: Use type guards for runtime type checking +6. **Avoid any**: Never use `any` or `unknown` types + +### Related Files +- [Code Example](../assets/AnyTypeError.ets) +- [ArkTS Type System](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-type-system) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/appstorage_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/appstorage_errors.md new file mode 100644 index 0000000000..2927bd95ab --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/appstorage_errors.md @@ -0,0 +1,95 @@ +# AppStorage Type Errors + +## Error: `AppStorage.get()` type parameter issue + +### Error Message +``` +Type inference errors with `AppStorage.get()` +``` + +### Cause +`AppStorage.get()` requires explicit type parameters or should be replaced with `setOrCreate` for proper type inference. ArkTS has strict type checking for generic functions. + +### Solution +Use `AppStorage.setOrCreate()` for initialization or provide explicit type parameters with `@StorageLink`. + +### Key Points +- Use `@StorageLink('key')` decorator for type-safe state binding +- Initialize with `AppStorage.setOrCreate('key', value)` in `aboutToAppear()` +- The decorator automatically handles type inference +- State is synchronized across all components using the same key + +### @StorageLink Pattern +```typescript +@Entry +@Component +struct MyComponent { + @StorageLink('myKey') myValue: number = 0; + + aboutToAppear() { + AppStorage.setOrCreate('myKey', 0); + } + + build() { + Text(`Value: ${this.myValue}`) + } +} +``` + +### AppStorage Methods +```typescript +// Set or create a value +AppStorage.setOrCreate(key: string, value: T): T + +// Get a value (requires type parameter) +AppStorage.get(key: string): T | undefined + +// Set a value +AppStorage.set(key: string, value: T): void + +// Delete a value +AppStorage.delete(key: string): void + +// Check if key exists +AppStorage.has(key: string): boolean +``` + +### State Decorators +```typescript +// Two-way binding with AppStorage +@StorageLink('key') value: Type = defaultValue; + +// One-way binding with AppStorage +@StorageProp('key') value: Type = defaultValue; + +// Two-way binding with LocalStorage +@LocalStorageLink('key') value: Type = defaultValue; + +// One-way binding with LocalStorage +@LocalStorageProp('key') value: Type = defaultValue; +``` + +### Best Practices +1. **Use @StorageLink**: Prefer decorators over direct AppStorage access +2. **Initialize properly**: Always call `setOrCreate` in `aboutToAppear()` +3. **Use unique keys**: Ensure keys are unique across the application +4. **Type safety**: Let the decorator handle type inference +5. **Clean up**: Delete values when no longer needed + +### Common Patterns +```typescript +// Counter pattern +@StorageLink('counter') count: number = 0; + +// User data pattern +@StorageLink('userName') userName: string = ''; + +// Settings pattern +@StorageLink('isDarkMode') isDarkMode: boolean = false; + +// Object pattern +@StorageLink('userSettings') settings: UserSettings = defaultSettings; +``` + +### Related Files +- [Code Example](../assets/AppStorageError.ets) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/arrow_function_conversion_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/arrow_function_conversion_errors.md new file mode 100644 index 0000000000..38c4eb82db --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/arrow_function_conversion_errors.md @@ -0,0 +1,163 @@ +# Function.bind 错误 + +## 错误描述 + +在 ArkTS 中,`Function.bind()` 方法不被支持。当尝试使用 `Function.bind()` 绑定 `this` 上下文时,会导致编译错误。 + +## 错误示例 + +```typescript +import { mediaquery, UIContext } from '@kit.ArkUI'; + +@Entry +@Component +struct BindError { + @State isWideScreen: boolean = false; + private mediaListener: mediaquery.MediaQueryListener | null = null; + + aboutToAppear() { + const mediaQuery = this.getUIContext().getMediaQuery(); + this.mediaListener = mediaQuery.matchMediaSync('(min-width: 600vp)'); + + if (this.mediaListener) { + this.mediaListener.on('change', this.onMediaQueryChange.bind(this)); + } + } + + private onMediaQueryChange(result: mediaquery.MediaQueryResult): void { + this.isWideScreen = result.matches; + } + + build() { + Column() { + Text(`屏幕宽度: ${this.isWideScreen ? '宽屏' : '窄屏'}`) + } + } +} +``` + +**错误信息:** +``` +Function.bind is not supported in ArkTS +``` + +## 解决方案 + +### 方案一:使用箭头函数 + +使用箭头函数替代 `Function.bind()`,箭头函数会自动捕获 `this` 上下文。 + +```typescript +@Entry +@Component +struct ArrowFunctionSolution { + @State isWideScreen: boolean = false; + private mediaListener: mediaquery.MediaQueryListener | null = null; + + aboutToAppear() { + const mediaQuery = this.getUIContext().getMediaQuery(); + this.mediaListener = mediaQuery.matchMediaSync('(min-width: 600vp)'); + + if (this.mediaListener) { + this.mediaListener.on('change', (result: mediaquery.MediaQueryResult) => { + this.isWideScreen = result.matches; + }); + } + } + + build() { + Column() { + Text(`屏幕宽度: ${this.isWideScreen ? '宽屏' : '窄屏'}`) + } + } +} +``` + +### 方案二:使用内联箭头函数 + +如果需要调用其他方法,可以在箭头函数内部调用。 + +```typescript +@Entry +@Component +struct InlineArrowFunctionSolution { + @State isWideScreen: boolean = false; + private mediaListener: mediaquery.MediaQueryListener | null = null; + + aboutToAppear() { + const mediaQuery = this.getUIContext().getMediaQuery(); + this.mediaListener = mediaQuery.matchMediaSync('(min-width: 600vp)'); + + if (this.mediaListener) { + this.mediaListener.on('change', (result: mediaquery.MediaQueryResult) => { + this.handleMediaQueryChange(result); + }); + } + } + + private handleMediaQueryChange(result: mediaquery.MediaQueryResult): void { + this.isWideScreen = result.matches; + } + + build() { + Column() { + Text(`屏幕宽度: ${this.isWideScreen ? '宽屏' : '窄屏'}`) + } + } +} +``` + +## 简单示例 + +```typescript +@Entry +@Component +struct ArrowFunctionExample { + @State count: number = 0; + + build() { + Column() { + Text(`计数: ${this.count}`) + .fontSize(20) + .margin({ bottom: 16 }) + + Button('增加') + .onClick(() => { + this.count++; + }) + } + .padding(16) + } +} +``` + +## 详细代码示例 + +- [ArrowFunctionConversionError.ets](../assets/ArrowFunctionConversionError.ets) - 完整的 Function.bind 错误修复示例,包含媒体查询监听和箭头函数使用 + +## 最佳实践 + +1. **使用箭头函数**:在需要保留 `this` 上下文的地方,优先使用箭头函数 +2. **避免 bind**:不要使用 `Function.bind()`,因为它在 ArkTS 中不被支持 +3. **内联处理简单逻辑**:对于简单的逻辑,可以直接在箭头函数中处理 +4. **提取复杂逻辑**:对于复杂的逻辑,可以提取为独立方法,然后在箭头函数中调用 + +## 常见错误 + +```typescript +// ❌ 错误:使用 Function.bind +this.mediaListener.on('change', this.onMediaQueryChange.bind(this)); + +// ❌ 错误:使用 Function.bind +button.onClick(this.handleClick.bind(this)); + +// ✅ 正确:使用箭头函数 +this.mediaListener.on('change', (result: mediaquery.MediaQueryResult) => { + this.isWideScreen = result.matches; +}); + +// ✅ 正确:使用箭头函数调用方法 +button.onClick(() => { + this.handleClick(); +}); +``` diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/avoid_area_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/avoid_area_type_errors.md new file mode 100644 index 0000000000..8d77f4f289 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/avoid_area_type_errors.md @@ -0,0 +1,59 @@ +# AvoidArea Type Error + +## 错误描述 + +在使用 `window.AvoidArea` 类型时,如果缺少 `visible` 属性,会导致类型错误。 + +## 错误示例 + +```typescript +@State avoidArea: window.AvoidArea = { + topRect: { left: 0, top: 0, width: 0, height: 0 }, + bottomRect: { left: 0, top: 0, width: 0, height: 0 }, + leftRect: { left: 0, top: 0, width: 0, height: 0 }, + rightRect: { left: 0, top: 0, width: 0, height: 0 } +}; +``` + +**错误信息:** +``` +Type '{ topRect: { left: number; top: number; width: number; height: number; }; bottomRect: { left: number; top: number; width: number; height: number; }; leftRect: { left: number; top: number; width: number; height: number; }; rightRect: { left: number; top: number; width: number; height: number; }; }' is missing the following properties from type 'AvoidArea': visible +``` + +## 解决方案 + +在 `AvoidArea` 对象中添加 `visible: false` 属性。 + +```typescript +@State avoidArea: window.AvoidArea = { + topRect: { left: 0, top: 0, width: 0, height: 0 }, + bottomRect: { left: 0, top: 0, width: 0, height: 0 }, + leftRect: { left: 0, top: 0, width: 0, height: 0 }, + rightRect: { left: 0, top: 0, width: 0, height: 0 }, + visible: false +}; +``` + +## 简单示例 + +```typescript +@Entry +@Component +struct SimpleExample { + @State avoidArea: window.AvoidArea = { + topRect: { left: 0, top: 0, width: 0, height: 0 }, + bottomRect: { left: 0, top: 0, width: 0, height: 0 }, + leftRect: { left: 0, top: 0, width: 0, height: 0 }, + rightRect: { left: 0, top: 0, width: 0, height: 0 }, + visible: false + }; + + build() { + Text(`顶部高度: ${this.avoidArea.topRect.height}`) + } +} +``` + +## 详细代码示例 + +- [AvoidAreaTypeError.ets](../assets/AvoidAreaTypeError.ets) - 完整的 AvoidArea 类型使用示例,包含窗口获取和避让区域监听 diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/breakpoint_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/breakpoint_type_errors.md new file mode 100644 index 0000000000..3b14ac795b --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/breakpoint_type_errors.md @@ -0,0 +1,122 @@ +# BreakpointType 类型错误 + +## 错误描述 + +在使用 GridRow 组件时,直接使用 `BreakpointType` 类型会导致类型不匹配错误。`BreakpointType` 是一个枚举类型,不能直接与字符串类型进行类型转换。 + +## 错误示例 + +```typescript +import { mediaquery, UIContext } from '@kit.ArkUI'; + +@Entry +@Component +struct BreakpointTypeError { + @State currentBreakpoint: string = 'sm'; + private smListener: mediaquery.MediaQueryListener | null = null; + private mdListener: mediaquery.MediaQueryListener | null = null; + private lgListener: mediaquery.MediaQueryListener | null = null; + + aboutToAppear() { + const mediaQuery = this.getUIContext().getMediaQuery(); + + this.smListener = mediaQuery.matchMediaSync('(0vp<=width<600vp)'); + this.mdListener = mediaQuery.matchMediaSync('(600vp<=width<840vp)'); + this.lgListener = mediaQuery.matchMediaSync('(840vp<=width)'); + + const smCallback = (result: mediaquery.MediaQueryResult): void => { + if (result.matches) { + this.currentBreakpoint = 'sm'; + } + }; + const mdCallback = (result: mediaquery.MediaQueryResult): void => { + if (result.matches) { + this.currentBreakpoint = 'md'; + } + }; + const lgCallback = (result: mediaquery.MediaQueryResult): void => { + if (result.matches) { + this.currentBreakpoint = 'lg'; + } + }; + + if (this.smListener) { + this.smListener.on('change', smCallback); + } + if (this.mdListener) { + this.mdListener.on('change', mdCallback); + } + if (this.lgListener) { + this.lgListener.on('change', lgCallback); + } + } + + aboutToDisappear() { + if (this.smListener) { + this.smListener.off('change'); + } + if (this.mdListener) { + this.mdListener.off('change'); + } + if (this.lgListener) { + this.lgListener.off('change'); + } + } + + build() { + Column() { + Text(`当前断点: ${this.currentBreakpoint}`) + } + } +} +``` + +## 解决方案 + +### 方案一:使用字符串类型 + +直接使用字符串类型来表示断点,而不是使用 `BreakpointType` 枚举类型。 + +```typescript +@State currentBreakpoint: string = 'sm'; +``` + +### 方案二:使用 SimpleBreakpointType + +如果需要使用类型定义,可以使用 `SimpleBreakpointType` 类型别名。 + +```typescript +type SimpleBreakpointType = 'sm' | 'md' | 'lg'; + +@State currentBreakpoint: SimpleBreakpointType = 'sm'; +``` + +## 简单示例 + +```typescript +@Entry +@Component +struct BreakpointExample { + @State currentBreakpoint: string = 'sm'; + + build() { + if (this.currentBreakpoint === 'lg') { + Row() { + Text('lg断点布局') + } + } else if (this.currentBreakpoint === 'md') { + Column() { + Text('md断点布局') + } + } else { + Column() { + Text('sm断点布局') + } + } + } +} +``` + +## 详细代码示例 + +- [BreakpointTypeError.ets](../assets/BreakpointTypeError.ets) - 完整的断点类型错误修复示例,包含媒体查询监听和断点判断逻辑 diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/catch_clause_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/catch_clause_type_errors.md new file mode 100644 index 0000000000..869c1680de --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/catch_clause_type_errors.md @@ -0,0 +1,89 @@ +# Catch Clause Variable Type Annotation Error + +## 错误描述 + +在 ArkTS 中,catch 子句的变量类型注解只能使用 `any` 或 `unknown`,不能使用其他具体类型。 + +## 错误示例 + +```typescript +try { + await someAsyncOperation(); +} catch (error: Error) { + console.error('发生错误:', error); +} +``` + +**错误信息:** +``` +Catch clause variable type annotation must be 'any' or 'unknown' if specified. +``` + +## 解决方案 + +### 方案1:移除类型注解(推荐) + +```typescript +try { + await someAsyncOperation(); +} catch (error) { + console.error('发生错误:', error); +} +``` + +### 方案2:使用 `any` 类型 + +```typescript +try { + await someAsyncOperation(); +} catch (error: any) { + console.error('发生错误:', error); +} +``` + +### 方案3:使用 `unknown` 类型(更安全) + +```typescript +try { + await someAsyncOperation(); +} catch (error: unknown) { + console.error('发生错误:', error); +} +``` + +## 详细说明 + +ArkTS 限制了 catch 子句中变量的类型注解,这是为了确保类型安全和代码的一致性。推荐的做法是: + +1. **不使用类型注解**:让 TypeScript 自动推断类型 +2. **使用 `unknown`**:如果必须使用类型注解,`unknown` 是最安全的选择,因为它要求在使用前进行类型检查 +3. **避免使用 `any`**:虽然允许,但会失去类型安全 + +## 简单示例 + +```typescript +import { camera } from '@kit.CameraKit'; + +@Entry +@Component +struct CameraExample { + private cameraManager: camera.CameraManager | null = null; + + async aboutToAppear() { + try { + const context = this.getUIContext().getHostContext() as common.UIAbilityContext; + this.cameraManager = camera.getCameraManager(context); + } catch (error) { + console.error('初始化相机失败:', error); + } + } + + build() { + Text('Camera Example') + } +} +``` + +## 详细代码示例 + +> [CatchClauseTypeError.ets](../assets/CatchClauseTypeError.ets) - 完整的 catch 子句类型注解错误示例和修复方案 diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/color_consistency_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/color_consistency_errors.md new file mode 100644 index 0000000000..9f383bbc8f --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/color_consistency_errors.md @@ -0,0 +1,209 @@ +# 颜色一致性错误解决方案 + +## 问题描述 + +在 HarmonyOS ArkUI 开发中,使用硬编码的颜色值(如 `#FFFFFF`、`#F5F5F5`)会触发编译警告: + +``` +It is recommended that you use layered parameters for easier color mode switching and theme color changing. +``` + +这个警告建议使用系统颜色资源,以便更好地支持深色模式切换和主题颜色变化。 + +## 问题示例 + +```typescript +// ❌ 不推荐:硬编码颜色 +Column() { + Text('内容') + .backgroundColor('#FFFFFF') // 警告 + .fontColor('#333333') // 警告 +} +``` + +## 推荐解决方案 + +使用系统颜色资源 `$r('sys.color.ohos_id_color_*')` 替代硬编码颜色: + +```typescript +// ✅ 推荐:使用系统颜色资源 +Column() { + Text('内容') + .backgroundColor($r('sys.color.ohos_id_color_background')) + .fontColor($r('sys.color.ohos_id_color_text_primary')) +} +``` + +## 常用系统颜色资源 + +| 系统颜色资源 | 用途 | 对应硬编码颜色 | +|------------|------|--------------| +| `ohos_id_color_background` | 背景色 | `#FFFFFF` | +| `ohos_id_color_sub_background` | 次级背景色 | `#F5F5F5` | +| `ohos_id_color_text_primary` | 主要文字颜色 | `#333333` | +| `ohos_id_color_text_secondary` | 次要文字颜色 | `#666666` | +| `ohos_id_color_text_tertiary` | 第三级文字颜色 | `#999999` | +| `ohos_id_color_list_separator` | 列表分割线 | `#E5E5E5` | +| `ohos_id_color_primary` | 主题主色 | `#007DFF` | +| `ohos_id_color_emphasize` | 强调色 | `#FF0000` | + +## 迁移步骤 + +### 1. 识别硬编码颜色 + +```typescript +// 查找代码中的硬编码颜色 +.backgroundColor('#FFFFFF') +.backgroundColor('#F5F5F5') +.backgroundColor('#007DFF') +``` + +### 2. 替换为系统颜色资源 + +```typescript +// 替换为对应的系统颜色 +.backgroundColor($r('sys.color.ohos_id_color_background')) +.backgroundColor($r('sys.color.ohos_id_color_sub_background')) +.backgroundColor($r('sys.color.ohos_id_color_primary')) +``` + +### 3. 处理特殊颜色 + +对于没有直接对应的系统颜色,可以使用 `ResourceColor` 类型: + +```typescript +// 自定义颜色 +.backgroundColor('#FF5722') + +// 或使用资源引用 +.backgroundColor($r('app.color.custom_background')) +``` + +## 完整示例 + +```typescript +@Entry +@Component +struct ColorConsistencyExample { + build() { + Column() { + // 卡片容器 + Column() { + Text('标题') + .fontSize(18) + .fontWeight(FontWeight.Bold) + .fontColor($r('sys.color.ohos_id_color_text_primary')) + + Text('这是内容描述') + .fontSize(14) + .fontColor($r('sys.color.ohos_id_color_text_secondary')) + .margin({ top: 8 }) + + Button('操作按钮') + .backgroundColor($r('sys.color.ohos_id_color_primary')) + .fontColor($r('sys.color.ohos_id_color_text_primary_contrast')) + .margin({ top: 16 }) + } + .width('100%') + .padding(16) + .backgroundColor($r('sys.color.ohos_id_color_background')) + .borderRadius(8) + } + .width('100%') + .height('100%') + .padding(16) + .backgroundColor($r('sys.color.ohos_id_color_sub_background')) + } +} +``` + +> [查看完整示例](../assets/ColorConsistencyError.ets) + +## 自定义颜色资源 + +如果需要使用自定义颜色,可以在 `resources/base/element/color.json` 中定义: + +```json +{ + "color": [ + { + "name": "custom_primary", + "value": "#007DFF" + }, + { + "name": "custom_background", + "value": "#F5F5F5" + } + ] +} +``` + +然后在代码中使用: + +```typescript +.backgroundColor($r('app.color.custom_primary')) +.backgroundColor($r('app.color.custom_background')) +``` + +## 深色模式适配 + +使用系统颜色资源会自动适配深色模式: + +```typescript +// 浅色模式:#FFFFFF +// 深色模式:#1A1A1A +.backgroundColor($r('sys.color.ohos_id_color_background')) + +// 浅色模式:#333333 +// 深色模式:#E5E5E5 +.fontColor($r('sys.color.ohos_id_color_text_primary')) +``` + +## 注意事项 + +1. **资源引用语法**:使用 `$r('sys.color.资源名')` 或 `$r('app.color.资源名')` + +2. **系统 vs 应用**:系统颜色使用 `sys.color.*`,应用自定义颜色使用 `app.color.*` + +3. **深色模式**:系统颜色会自动适配深色模式,无需手动处理 + +4. **主题切换**:系统颜色会跟随系统主题变化,提供更好的用户体验 + +5. **兼容性**:系统颜色资源从 API 9 开始支持 + +## 相关 API 参考 + +- [系统颜色资源](https://developer.harmonyos.com/cn/docs/documentation/references/arkui-ts-resource-color) +- [资源管理](https://developer.harmonyos.com/cn/docs/documentation/references/arkui-ts-resource-manager) +- [深色模式适配](https://developer.harmonyos.com/cn/docs/documentation/guides/arkui-ts-dark-mode) + +## 迁移检查清单 + +- [ ] 识别所有硬编码颜色值 +- [ ] 将硬编码颜色替换为系统颜色资源 +- [ ] 测试浅色模式下的显示效果 +- [ ] 测试深色模式下的显示效果 +- [ ] 验证主题切换是否正常 +- [ ] 确认编译警告已消失 + +## 常见问题 + +### Q: 为什么要使用系统颜色资源? + +A: 系统颜色资源可以自动适配深色模式和主题切换,提供更好的用户体验,同时减少维护成本。 + +### Q: 所有颜色都必须使用系统资源吗? + +A: 不是。对于品牌色等特殊颜色,可以使用自定义颜色资源或硬编码值。但对于通用UI元素,建议使用系统颜色。 + +### Q: 如何在深色模式下使用不同的颜色? + +A: 使用系统颜色资源会自动处理深色模式。如果需要自定义深色模式颜色,可以在 `resources/dark/element/color.json` 中定义。 + +### Q: 可以混合使用系统颜色和硬编码颜色吗? + +A: 可以,但不推荐。建议统一使用系统颜色资源以保持一致性。 + +### Q: 如何查看所有可用的系统颜色资源? + +A: 参考 HarmonyOS 官方文档中的系统颜色资源列表,或使用 DevEco Studio 的代码提示查看可用资源。 diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/color_property_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/color_property_errors.md new file mode 100644 index 0000000000..097e1b736c --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/color_property_errors.md @@ -0,0 +1,136 @@ +# Color Property Errors + +## Error: `Property 'XXX' does not exist on type 'typeof Color'` + +### Error Message +``` +Property 'LightBlue' does not exist on type 'typeof Color' +``` + +### Cause +The `Color` class in ArkTS does not have all of the color properties that might be expected. Some color names like `LightBlue`, `DarkGray`, etc. are not available. Using these properties will cause compilation errors. + +### Solution +Use hex color values (`#RRGGBB` or `#AARRGGBB`) instead of non-existent `Color` properties. + +### Key Points +- Use hex color values instead of non-existent Color properties +- Hex format: `#RRGGBB` for RGB, `#AARRGGBB` for ARGB +- Common colors: `#FF0000` (red), `#00FF00` (green), `#0000FF` (blue) +- Use layered parameters for theme-aware colors + +### Basic Pattern +```typescript +// ❌ Wrong: Using non-existent Color property +Text('Hello') + .fontColor(Color.LightBlue) + +// ✅ Correct: Using hex color value +Text('Hello') + .fontColor('#ADD8E6') +``` + +### Common Color Replacements +```typescript +// Light colors +Color.LightBlue -> '#ADD8E6' +Color.LightGreen -> '#90EE90' +Color.LightGray -> '#D3D3D3' +Color.LightCyan -> '#E0FFFF' + +// Dark colors +Color.DarkBlue -> '#00008B' +Color.DarkGreen -> '#006400' +Color.DarkGray -> '#A9A9A9' +Color.DarkCyan -> '#008B8B' + +// Other colors +Color.Pink -> '#FFC0CB' +Color.Orange -> '#FFA500' +Color.Purple -> '#800080' +Color.Brown -> '#A52A2A' +Color.Gold -> '#FFD700' +Color.Silver -> '#C0C0C0' +``` + +### Common Hex Colors +```typescript +// Primary colors +'#FF0000' // Red +'#00FF00' // Green +'#0000FF' // Blue + +// Secondary colors +'#FFFF00' // Yellow +'#FF00FF' // Magenta +'#00FFFF' // Cyan + +// Grayscale +'#FFFFFF' // White +'#F5F5F5' // Light gray +'#CCCCCC' // Medium gray +'#999999' // Dark gray +'#666666' // Darker gray +'#333333' // Very dark gray +'#000000' // Black + +// UI colors +'#007DFF' // Primary blue +'#FF6B00' // Orange +'#00C853' // Green +'#FF1744' // Red +'#651FFF' // Purple +'#00B0FF' // Light blue +``` + +### Color Properties +```typescript +// Text color +.fontColor('#FF0000') + +// Background color +.backgroundColor('#00FF00') + +// Border color +.borderColor('#0000FF') + +// Shadow color +.shadow({ radius: 10, color: '#FF0000' }) +``` + +### Detailed Examples +For more detailed code examples, see: +- [Basic Color Usage](../assets/ColorPropertyError.ets#L8-L12) +- [Text Color Pattern](../assets/ColorPropertyError.ets#L14-L18) +- [Background Color Pattern](../assets/ColorPropertyError.ets#L20-L24) +- [Theme Switching](../assets/ColorPropertyError.ets#L26-L36) + +### Best Practices +1. **Use hex values**: Always use hex color values instead of non-existent Color properties +2. **Define constants**: Create color constants for reuse +3. **Use resources**: Use resource references for theme-aware colors +4. **Document colors**: Add comments explaining color choices +5. **Test contrast**: Ensure text has sufficient contrast with background + +### Common Mistakes +```typescript +// ❌ Wrong: Using non-existent Color property +Text('Hello') + .fontColor(Color.LightBlue) + +// ❌ Wrong: Using non-existent Color property +Text('Hello') + .fontColor(Color.DarkGray) + +// ✅ Correct: Using hex color value +Text('Hello') + .fontColor('#ADD8E6') + +// ✅ Correct: Using hex color value +Text('Hello') + .fontColor('#A9A9A9') +``` + +### Related Files +- [Code Example](../assets/ColorPropertyError.ets) +- [ArkTS UI Components](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-uicomponent) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/context_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/context_type_errors.md new file mode 100644 index 0000000000..5c1f156950 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/context_type_errors.md @@ -0,0 +1,260 @@ +# Context 类型错误解决方案 + +## 问题描述 + +在 HarmonyOS ArkUI 开发中,使用 `this.getUIContext().getHostContext()` 获取的 Context 类型为 `Context | undefined`,直接传递给需要 `Context` 类型参数的函数会触发类型错误: + +``` +Argument of type 'Context | undefined' is not assignable to parameter of type 'Context'. +``` + +## 问题示例 + +```typescript +// ❌ 错误:Context 可能为 undefined +const context = this.getUIContext().getHostContext(); +await window.getLastWindow(context); // 类型错误 +``` + +## 推荐解决方案 + +### 方案 1: 添加 null 检查 + +```typescript +// ✅ 推荐:添加 null 检查 +const context = this.getUIContext().getHostContext(); +if (context) { + await window.getLastWindow(context); +} +``` + +### 方案 2: 使用类型断言 + +```typescript +// ✅ 可选:使用类型断言 +const context = this.getUIContext().getHostContext() as common.UIAbilityContext; +await window.getLastWindow(context); +``` + +### 方案 3: 使用可选链和空值合并 + +```typescript +// ✅ 可选:使用可选链 +const context = this.getUIContext().getHostContext(); +if (context) { + await window.getLastWindow(context); +} +``` + +## 迁移步骤 + +### 1. 识别 Context 类型错误 + +```typescript +// 查找代码中的 Context 使用 +const context = this.getUIContext().getHostContext(); +window.getLastWindow(context); // 类型错误 +``` + +### 2. 添加 null 检查 + +```typescript +const context = this.getUIContext().getHostContext(); +if (context) { + window.getLastWindow(context); // 正确 +} +``` + +### 3. 使用类型断言(可选) + +```typescript +const context = this.getUIContext().getHostContext() as common.UIAbilityContext; +window.getLastWindow(context); // 正确 +``` + +## 完整示例 + +```typescript +import { window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +@Entry +@Component +struct ContextTypeExample { + @State windowWidth: number = 0; + @State windowHeight: number = 0; + + async aboutToAppear() { + // ✅ 正确:添加 null 检查 + const context = this.getUIContext().getHostContext(); + if (context) { + await this.getWindowSize(context as common.UIAbilityContext); + } + } + + private async getWindowSize(context: common.UIAbilityContext) { + try { + const win = await window.getLastWindow(context); + const properties = win.getWindowProperties(); + this.windowWidth = properties.windowRect.width; + this.windowHeight = properties.windowRect.height; + } catch (err) { + console.error('获取窗口大小失败:', err); + } + } + + build() { + Column() { + Text(`窗口大小: ${this.windowWidth} x ${this.windowHeight}`) + .fontSize(16) + } + .width('100%') + .height('100%') + .padding(16) + } +} +``` + +> [查看完整示例](../assets/ContextTypeError.ets) + +## 使用场景 + +### 1. 窗口操作 + +```typescript +async operateWindow() { + const context = this.getUIContext().getHostContext(); + if (context) { + const win = await window.getLastWindow(context); + await win.resize(800, 600); + } +} +``` + +### 2. 媒体查询 + +```typescript +setupMediaQuery() { + const context = this.getUIContext().getHostContext(); + if (context) { + const mediaQuery = this.getUIContext().getMediaQuery(); + const listener = mediaQuery.matchMediaSync('(min-width: 600vp)'); + listener.on('change', (result) => { + console.info(`Matches: ${result.matches}`); + }); + } +} +``` + +### 3. 文件操作 + +```typescript +async readFile() { + const context = this.getUIContext().getHostContext(); + if (context) { + const filesDir = context.filesDir; + // 读取文件操作 + } +} +``` + +## 最佳实践 + +### 1. 始终添加 null 检查 + +```typescript +const context = this.getUIContext().getHostContext(); +if (!context) { + console.error('Context is undefined'); + return; +} +// 使用 context +``` + +### 2. 使用类型断言明确类型 + +```typescript +const context = this.getUIContext().getHostContext() as common.UIAbilityContext; +``` + +### 3. 封装 Context 获取 + +```typescript +private getSafeContext(): common.UIAbilityContext | null { + const context = this.getUIContext().getHostContext(); + return context ? context as common.UIAbilityContext : null; +} + +async operateWindow() { + const context = this.getSafeContext(); + if (context) { + const win = await window.getLastWindow(context); + // 操作窗口 + } +} +``` + +### 4. 处理异步操作 + +```typescript +async asyncOperation() { + const context = this.getUIContext().getHostContext(); + if (context) { + try { + const win = await window.getLastWindow(context); + // 异步操作 + } catch (err) { + console.error('操作失败:', err); + } + } +} +``` + +## 注意事项 + +1. **类型定义**:`getHostContext()` 返回 `Context | undefined`,必须处理 undefined 情况 + +2. **null 检查**:建议使用 `if (context)` 检查,而不是直接使用 + +3. **类型断言**:使用 `as common.UIAbilityContext` 明确类型,但确保类型正确 + +4. **异常处理**:在异步操作中添加 try-catch 处理可能的异常 + +5. **封装复用**:可以将 Context 获取逻辑封装为工具函数 + +## 相关 API 参考 + +- [UIContext.getHostContext()](https://developer.harmonyos.com/cn/docs/documentation/reference/arkui-ts/ts-methods-uicontext) +- [Context](https://developer.harmonyos.com/cn/docs/documentation/reference/apis-js-apis-application-context) +- [window.getLastWindow()](https://developer.harmonyos.com/cn/docs/documentation/reference/apis-arkui-window-0000001774529169) + +## 迁移检查清单 + +- [ ] 识别所有使用 `getHostContext()` 的地方 +- [ ] 添加 null 检查处理 undefined 情况 +- [ ] 使用类型断言明确 Context 类型 +- [ ] 添加异常处理 +- [ ] 测试功能是否正常 +- [ ] 确认类型错误已解决 + +## 常见问题 + +### Q: 为什么 `getHostContext()` 可能返回 undefined? + +A: 在某些情况下(如组件未完全初始化),Context 可能尚未准备好,因此返回 undefined。 + +### Q: 可以使用非空断言 `!` 吗? + +A: 可以,但不推荐。使用 `!` 会跳过类型检查,可能导致运行时错误。建议使用 null 检查。 + +### Q: 什么时候应该使用类型断言? + +A: 当你确定 Context 的具体类型(如 `UIAbilityContext`)时,可以使用类型断言提高代码可读性。 + +### Q: 如何在多个地方复用 Context? + +A: 可以封装一个工具函数或在组件中定义一个私有方法来安全地获取 Context。 + +### Q: null 检查会影响性能吗? + +A: 不会。null 检查是非常轻量级的操作,对性能影响可以忽略不计。 diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/decorator_state_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/decorator_state_errors.md new file mode 100644 index 0000000000..25a06a26ea --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/decorator_state_errors.md @@ -0,0 +1,109 @@ +# @State 装饰器错误 + +## 错误描述 + +在 ArkTS 中,`@State` 装饰器只能用于 `@Component` 修饰的 struct 中,不能用于普通 class。 + +## 错误示例 + +```typescript +// ❌ AI 经常生成的错误代码 +class FoldableStateManager { + @State currentBreakpoint: string = 'sm'; + @State isFolded: boolean = true; + + aboutToAppear() { + // ... + } +} +``` + +**错误信息:** +``` +The '@State' decorator can only be used with 'struct'. +``` + +## 原因分析 + +`@State` 是 ArkUI 框架的状态管理装饰器,用于在组件内部管理状态。它只能与 `@Component` 一起使用,不能用于普通类。 + +## 解决方案 + +### 方案1:移除 @State 装饰器(推荐用于非组件类) + +如果类不需要响应式状态管理,移除 `@State` 装饰器: + +```typescript +// ✅ 正确的代码 +class FoldableStateManager { + currentBreakpoint: string = 'sm'; + isFolded: boolean = true; + + aboutToAppear() { + // ... + } +} +``` + +### 方案2:使用 @Component struct(推荐用于状态管理) + +如果需要状态管理,将类改为 @Component struct: + +```typescript +// ✅ 正确的代码 +@Component +struct FoldableStateManager { + @State currentBreakpoint: string = 'sm'; + @State isFolded: boolean = true; + + aboutToAppear() { + // ... + } + + build() { + // UI 组件 + } +} +``` + +## 简单示例 + +### 用于工具类 + +```typescript +// ✅ 用于工具类时移除 @State +class DisplayManager { + currentBreakpoint: string = 'sm'; + + updateBreakpoint(width: number): string { + if (width < 600) return 'sm'; + if (width < 840) return 'md'; + return 'lg'; + } +} +``` + +### 用于 @Component struct + +```typescript +// ✅ 用于组件时保留 @State +@Component +struct AdaptiveLayout { + @State currentBreakpoint: string = 'sm'; + + build() { + Column() { + Text(`Current: ${this.currentBreakpoint}`) + } + } +} +``` + +## 详细代码示例 + +> [DecoratorStateError.ets](../assets/DecoratorStateError.ets) - 完整的 @State 装饰器错误示例和修复方案 + +## 相关文档 + +- [ArkTS 状态管理](./state_migration.md) +- [HarmonyOS 官方文档](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-management-overview) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/display_listener_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/display_listener_type_errors.md new file mode 100644 index 0000000000..3108f29ba3 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/display_listener_type_errors.md @@ -0,0 +1,149 @@ +# Display Listener Type Errors + +## Error: Display on/off method type error + +### Error Message +``` +Type 'void' is not assignable to type 'number' +Argument of type 'number' is not assignable to parameter of type 'Callback' +``` + +### Cause +The `display.on()` and `display.off()` methods are module-level methods, not instance methods of the `Display` class. Attempting to call them on a `Display` object instance causes type errors. + +### Solution +Use `display.on()` and `display.off()` at the module level for listening to display changes. Use `Display.on()` and `Display.off()` on Display instances for instance-specific events like `availableAreaChange`. + +### Key Points +- `display.on('add'|'remove'|'change', callback)` - Module level methods +- `Display.on('availableAreaChange', callback)` - Instance level methods +- `Display.on('foldStatusChange', callback)` - Instance level methods +- `Display.on('captureStatusChange', callback)` - Instance level methods + +### Module Level Listeners +```typescript +import { display } from '@kit.ArkUI'; + +// ✅ Correct: Module level listener +let listener: (data: number) => void = (data: number) => { + console.info(`Display changed, ID: ${data}`); +}; + +display.on('change', listener); + +// Unregister +display.off('change', listener); +``` + +### Instance Level Listeners +```typescript +import { display } from '@kit.ArkUI'; + +const displayClass = display.getDefaultDisplaySync(); + +// ✅ Correct: Instance level listener +let areaListener: (data: display.Rect) => void = (data: display.Rect) => { + console.info(`Available area changed: ${JSON.stringify(data)}`); +}; + +displayClass.on('availableAreaChange', areaListener); + +// Unregister +displayClass.off('availableAreaChange', areaListener); +``` + +### ❌ Wrong Usage +```typescript +const displayClass = display.getDefaultDisplaySync(); + +// ❌ Wrong: Calling on() on Display instance +let listener = displayClass.on('change', () => { + console.info('Display changed'); +}); + +// ❌ Wrong: Calling off() on Display instance +displayClass.off('change', listener); +``` + +### ✅ Correct Usage +```typescript +// ✅ Correct: Module level for display changes +let changeListener: (data: number) => void = (data: number) => { + console.info(`Display changed, ID: ${data}`); +}; +display.on('change', changeListener); + +// ✅ Correct: Instance level for available area changes +const displayClass = display.getDefaultDisplaySync(); +let areaListener: (data: display.Rect) => void = (data: display.Rect) => { + console.info(`Available area changed: ${JSON.stringify(data)}`); +}; +displayClass.on('availableAreaChange', areaListener); +``` + +### Available Listener Types + +#### Module Level (display module) +- `on('add', callback: Callback)` - Display added +- `on('remove', callback: Callback)` - Display removed +- `on('change', callback: Callback)` - Display changed + +#### Instance Level (Display object) +- `on('availableAreaChange', callback: Callback)` - Available area changed +- `on('foldStatusChange', callback: Callback)` - Fold status changed +- `on('captureStatusChange', callback: Callback)` - Capture status changed + +### Best Practices +1. **Use module level** for display add/remove/change events +2. **Use instance level** for display-specific events +3. **Always unregister** listeners in aboutToDisappear() +4. **Store listener references** for proper cleanup +5. **Use explicit types** for listener callbacks + +### Component Integration +```typescript +@Entry +@Component +struct DisplayListenerExample { + private changeListener?: (data: number) => void; + private areaListener?: (data: display.Rect) => void; + + aboutToAppear() { + // Register module level listener + this.changeListener = (data: number) => { + console.info(`Display changed: ${data}`); + }; + display.on('change', this.changeListener); + + // Register instance level listener + const displayClass = display.getDefaultDisplaySync(); + this.areaListener = (data: display.Rect) => { + console.info(`Area changed: ${JSON.stringify(data)}`); + }; + displayClass.on('availableAreaChange', this.areaListener); + } + + aboutToDisappear() { + // Unregister listeners + if (this.changeListener) { + display.off('change', this.changeListener); + this.changeListener = undefined; + } + if (this.areaListener) { + const displayClass = display.getDefaultDisplaySync(); + displayClass.off('availableAreaChange', this.areaListener); + this.areaListener = undefined; + } + } + + build() { + Column() { + Text('Display Listener Example') + } + } +} +``` + +### Related Files +- [Code Example](../assets/DisplayListenerTypeError.ets) +- [Display API Documentation](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-display) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/duplicate_entry_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/duplicate_entry_errors.md new file mode 100644 index 0000000000..53ea1a4a2a --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/duplicate_entry_errors.md @@ -0,0 +1,193 @@ +# 多个 @Entry 装饰器错误 + +## 问题描述 + +在 ArkTS 中,一个 `.ets` 文件只能有一个 `@Entry` 装饰器。如果在同一个文件中使用多个 `@Entry` 装饰器,会导致编译错误。 + +### 错误信息 + +``` +Duplicate entry annotation +``` + +或 + +``` +More than one @Entry decorator in the same file +``` + +## 错误示例 + +```typescript +// ❌ 错误:一个文件中有两个 @Entry +@Entry +@Component +struct FirstPage { + build() { + Column() { + Text('Page 1') + } + } +} + +@Entry +@Component +struct SecondPage { + build() { + Column() { + Text('Page 2') + } + } +} +``` + +## 解决方案 + +### 方案一:将子组件改为普通 @Component(推荐) + +将多余的 `@Entry` 改为普通 `@Component`,避免重复的入口声明。 + +```typescript +// ✅ 正确:只有一个 @Entry,其他用 @Component +@Entry +@Component +struct MainPage { + build() { + Column() { + Text('Main Page') + ChildComponent() + } + } +} + +@Component +struct ChildComponent { + build() { + Text('Child Component') + } +} +``` + +### 方案二:拆分到不同文件 + +将每个页面组件拆分到独立的文件中。 + +```typescript +// mainPage.ets +@Entry +@Component +struct MainPage { + build() { + Column() { + Text('Main Page') + } + } +} +``` + +```typescript +// subPage.ets - 单独的文件 +@Entry +@Component +struct SubPage { + build() { + Column() { + Text('Sub Page') + } + } +} +``` + +### 方案三:使用状态管理实现多页面 + +如果需要在多个视图之间切换,可以使用 `@State` 和条件渲染: + +```typescript +@Entry +@Component +struct MultiViewPage { + @State currentView: number = 0; + + build() { + Column() { + if (this.currentView === 0) { + ViewOne() + } else { + ViewTwo() + } + + Row() { + Button('View 1') + .onClick(() => this.currentView = 0) + Button('View 2') + .onClick(() => this.currentView = 1) + } + } + } +} + +@Component +struct ViewOne { + build() { + Text('View 1').fontSize(24) + } +} + +@Component +struct ViewTwo { + build() { + Text('View 2').fontSize(24) + } +} +``` + +## 简单示例 + +```typescript +@Entry +@Component +struct PageExample { + @State message: string = 'Hello'; + + build() { + Column() { + Text(this.message) + .fontSize(24) + + // 使用 @Component 装饰的子组件 + ContentSection() + } + .width('100%') + .height('100%') + .padding(20) + } +} + +@Component +struct ContentSection { + @State count: number = 0; + + build() { + Column() { + Text(`Count: ${this.count}`) + .fontSize(18) + + Button('Add') + .onClick(() => { + this.count++; + }) + } + } +} +``` + +## 详细代码示例 + +- [DuplicateEntryError.ets](../assets/DuplicateEntryError.ets) - 完整的多个 @Entry 错误修复示例 + +## 最佳实践 + +1. **每个文件一个 @Entry**:保持每个 `.ets` 文件只有一个入口组件 +2. **使用 @Component 复用 UI**:使用 `@Component` 装饰器创建可复用的 UI 组件 +3. **合理拆分文件**:将不同页面拆分到独立文件,便于管理和维护 +4. **状态提升**:如果多个组件需要共享状态,将状态提升到共同的父组件中 diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/esobject_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/esobject_type_errors.md new file mode 100644 index 0000000000..0a7d0ce4fe --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/esobject_type_errors.md @@ -0,0 +1,110 @@ +# ESObject 类型限制错误 + +## 错误描述 + +在 ArkTS 中,`ESObject` 类型的使用受到限制,不能直接用作变量类型声明。这是为了确保类型安全和避免运行时错误。 + +## 错误示例 + +```typescript +let nfcModule: ESObject = null; +``` + +**错误信息:** +``` +Usage of "ESObject" type is restricted (arkts-limited-esobj) +``` + +## 解决方案 + +### 方案1:使用动态导入(推荐) + +对于需要动态导入的模块,使用 `import()` 函数并使用 `Promise` 或 `ESModule` 类型。 + +```typescript +let nfcModule: ESModule | null = null; +try { + nfcModule = import('@kit.ConnectivityKit'); +} catch (err) { + console.error(`Failed to import NFC module: ${JSON.stringify(err)}`); +} +``` + +### 方案2:使用具体类型 + +如果知道模块的具体类型,使用具体的接口或类。 + +```typescript +import { nfcController } from '@kit.ConnectivityKit'; + +let nfcControllerInstance: nfcController.NfcController | null = null; +``` + +### 方案3:使用 unknown 类型 + +如果不确定模块类型,可以使用 `unknown` 类型,但需要在使用时进行类型检查。 + +```typescript +let module: unknown = null; +try { + module = import('@kit.ConnectivityKit'); + if (module !== null && module !== undefined) { + // 使用前进行类型检查 + console.info('Module imported successfully'); + } +} catch (err) { + console.error(`Failed to import module: ${JSON.stringify(err)}`); +} +``` + +## 详细说明 + +ArkTS 限制 `ESObject` 类型的使用是为了: + +1. **类型安全**:避免运行时类型错误 +2. **编译时检查**:确保类型使用的正确性 +3. **模块系统规范**:遵循 ES 模块标准 + +## 简单示例 + +```typescript +@Entry +@Component +struct DynamicImportExample { + @State moduleLoaded: boolean = false; + @State moduleName: string = ''; + + async loadNFCModule() { + try { + const nfcModule = await import('@kit.ConnectivityKit'); + this.moduleLoaded = true; + this.moduleName = 'NFC'; + console.info('NFC module imported successfully'); + } catch (err) { + console.error(`Failed to import NFC module: ${JSON.stringify(err)}`); + this.moduleLoaded = false; + this.moduleName = 'NFC'; + } + } + + build() { + Column() { + Button('Load NFC Module') + .onClick(() => { + this.loadNFCModule(); + }) + .margin(20) + + if (this.moduleLoaded) { + Text(`${this.moduleName} module loaded successfully`) + .fontSize(16) + .margin(20) + } + } + } +} +``` + +## 详细代码示例 + +> [ESObjectTypeError.ets](../assets/ESObjectTypeError.ets) - 完整的ESObject类型错误示例和修复方案 diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/fontcolor_property_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/fontcolor_property_errors.md new file mode 100644 index 0000000000..7433c81f91 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/fontcolor_property_errors.md @@ -0,0 +1,104 @@ +# fontColor 属性错误 + +## 错误描述 + +`fontColor` 属性只能用于文本组件(如 `Text`、`Span`、`Button` 等),不能用于容器组件(如 `Column`、`Row`、`Stack` 等)。在容器组件上使用 `fontColor` 会导致编译错误。 + +## 错误示例 + +```typescript +@Entry +@Component +struct FontColorError { + build() { + Column() { + Text('Hello World') + } + .fontColor('#FF0000') + } +} +``` + +**错误信息:** +``` +Property 'fontColor' does not exist on type 'ColumnAttribute' +``` + +## 解决方案 + +### 方案一:将 fontColor 应用到 Text 组件 + +将 `fontColor` 属性从容器组件移除,直接应用到文本组件上。 + +```typescript +@Entry +@Component +struct FontColorCorrect { + build() { + Column() { + Text('Hello World') + .fontColor('#FF0000') + } + } +} +``` + +## 简单示例 + +```typescript +@Entry +@Component +struct FontColorExample { + build() { + Column() { + Text('标题') + .fontSize(20) + .fontWeight(FontWeight.Bold) + .fontColor('#333333') + + Text('内容') + .fontSize(16) + .fontColor('#666666') + } + .width('100%') + .padding(16) + } +} +``` + +## 详细代码示例 + +- [FontColorPropertyError.ets](../assets/FontColorPropertyError.ets) - 完整的 fontColor 属性错误修复示例,包含正确和错误的用法对比 + +## 最佳实践 + +1. **仅在文本组件上使用 fontColor**:`fontColor` 属性应该只用于 `Text`、`Span`、`Button` 等文本组件 +2. **使用十六进制颜色值**:推荐使用十六进制颜色值(如 `#FF0000`)而不是 `Color` 枚举 +3. **定义颜色常量**:对于重复使用的颜色,建议定义常量 +4. **使用资源引用**:对于主题相关的颜色,使用资源引用以便于主题切换 + +## 常见错误 + +```typescript +// ❌ 错误:在 Column 上使用 fontColor +Column() { + Text('Hello') +} +.fontColor('#FF0000') + +// ❌ 错误:在 Row 上使用 fontColor +Row() { + Text('Hello') +} +.fontColor('#FF0000') + +// ✅ 正确:在 Text 上使用 fontColor +Column() { + Text('Hello') + .fontColor('#FF0000') +} + +// ✅ 正确:在 Button 上使用 fontColor +Button('Click Me') + .fontColor('#FFFFFF') +``` diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/function_return_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/function_return_type_errors.md new file mode 100644 index 0000000000..10f9dceaad --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/function_return_type_errors.md @@ -0,0 +1,194 @@ +# Function Return Type Errors + +## Error: `Object literals cannot be used as type declarations` + +### Error Message +``` +Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types) +``` + +### Cause +ArkTS does not allow object literal types (like `{ width: number; height: number }`) to be used directly as return type annotations. You must define an interface or type alias first. + +### Solution +Define an interface for the object shape, then use that interface as the return type. + +### Basic Pattern +```typescript +// ❌ Wrong: Object literal type in return annotation +private getWindowInfo(): { width: number; height: number } { + return { + width: 1080, + height: 2340 + }; +} + +// ✅ Correct: Define interface first +interface WindowInfo { + width: number; + height: number; +} + +private getWindowInfo(): WindowInfo { + return { + width: 1080, + height: 2340 + }; +} +``` + +### Simple Example +```typescript +interface WindowInfo { + width: number; + height: number; +} + +@Entry +@Component +struct WindowInfoExample { + @State windowWidth: number = 0; + @State windowHeight: number = 0; + + private getWindowInfo(): WindowInfo { + return { + width: 1080, + height: 2340 + }; + } + + build() { + Column() { + Text(`Window: ${this.windowWidth} x ${this.windowHeight}`) + } + } +} +``` + +### Detailed Code Examples +- [FunctionReturnTypeError.ets](../assets/FunctionReturnTypeError.ets) - 完整的函数返回类型示例,包含接口定义和使用 + +--- + +## Error: `Function return type inference is limited` + +### Error Message +``` +Function return type inference is limited +``` + +### Cause +ArkTS has limited type inference for function return types, especially for complex types or when the return type cannot be easily inferred from the function body. This is a strict type safety requirement to ensure code clarity and prevent type errors. + +### Solution +Add explicit return type annotations to functions that return complex types or when the compiler cannot infer the return type. + +### Key Points +- Add explicit return type annotations for complex return types +- Use interface or type aliases for object return types +- Annotate functions that return arrays, objects, or unions +- Keep return type annotations simple and clear + +### Basic Pattern +```typescript +// ❌ Wrong: No explicit return type +private getWindowInfo() { + return { + width: 1080, + height: 2340 + }; +} + +// ✅ Correct: With explicit return type +private getWindowInfo(): { width: number; height: number } { + return { + width: 1080, + height: 2340 + }; +} +``` + +### Common Patterns +```typescript +// Object return types +private getConfig(): Config { + return { width: 100, height: 100, color: 'red' }; +} + +// Array return types +interface Item { + id: number; + name: string; +} + +private getItems(): Item[] { + return [ + { id: 1, name: 'Item 1' }, + { id: 2, name: 'Item 2' } + ]; +} + +// Union return types +private getValue(): string | number { + if (Math.random() > 0.5) { + return 'string'; + } + return 42; +} + +// Optional return types +private findUser(id: number): User | undefined { + return this.users.find(user => user.id === id); +} + +// Promise return types +private async fetchData(): Promise { + const response = await fetch('https://api.example.com/data'); + return await response.json(); +} +``` + +### Detailed Examples +For more detailed code examples, see: +- [Object Return Type](../assets/FunctionReturnTypeError.ets#L14-L19) +- [Void Return Type](../assets/FunctionReturnTypeError.ets#L21-L26) +- [Usage Pattern](../assets/FunctionReturnTypeError.ets#L28-L35) + +### Best Practices +1. **Add explicit types**: Always add return type annotations for complex functions +2. **Use interfaces**: Define interfaces for object return types +3. **Keep types simple**: Avoid overly complex return type expressions +4. **Be consistent**: Use the same style throughout your codebase +5. **Document types**: Add comments explaining complex return types + +### Common Mistakes +```typescript +// ❌ Wrong: No explicit return type +private getConfig() { + return { width: 100, height: 100 }; +} + +// ❌ Wrong: Using 'any' type +private getConfig(): any { + return { width: 100, height: 100 }; +} + +// ✅ Correct: With explicit return type +private getConfig(): { width: number; height: number } { + return { width: 100, height: 100 }; +} + +// ✅ Correct: Using interface +interface Config { + width: number; + height: number; +} + +private getConfig(): Config { + return { width: 100, height: 100 }; +} +``` + +### Related Files +- [Code Example](../assets/FunctionReturnTypeError.ets) +- [ArkTS Language Guide](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-get-started) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/idata_source_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/idata_source_errors.md new file mode 100644 index 0000000000..ab4341790f --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/idata_source_errors.md @@ -0,0 +1,77 @@ +# IDataSource 类型错误 + +## 错误信息 + +``` +Argument of type 'string[]' is not assignable to parameter of type 'IDataSource'. +Type 'string[]' is missing the following properties from type 'IDataSource': + totalCount, getData, registerDataChangeListener, unregisterDataChangeListener +``` + +## 错误原因 + +在 ArkTS 中,`LazyForEach` 要求数据源必须实现 `IDataSource` 接口,直接使用 `string[]` 会导致类型错误。 + +## 错误示例 + +```typescript +// 错误 - 直接使用 string[] +private data: string[] = ['Item 1', 'Item 2', 'Item 3']; + +build() { + List() { + LazyForEach(this.data, (item: string) => { + ListItem() { + Text(item) + } + }, (item: string) => item) + } +} +``` + +## 解决方案 + +实现 `IDataSource` 接口: + +```typescript +class MyDataSource { + data: string[] = []; + private listeners: DataChangeListener[] = []; + + totalCount(): number { + return this.data.length; + } + + getData(index: number): string { + return this.data[index]; + } + + registerDataChangeListener(listener: DataChangeListener): void { + this.listeners.push(listener); + } + + unregisterDataChangeListener(listener: DataChangeListener): void { + const index = this.listeners.indexOf(listener); + if (index > -1) { + this.listeners.splice(index, 1); + } + } + + pushData(data: string): void { + this.data.push(data); + this.listeners.forEach((listener: DataChangeListener) => { + listener.onDataAdd(this.data.length - 1); + }); + } +} +``` + +## 详细代码示例 + +请参考 [IDataSourceError.ets](../assets/IDataSourceError.ets) + +## 相关 API + +- [LazyForEach](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/arkts-rendering-control-000000177275贼299) +- [IDataSource](https://developer.harmonyos.com/cn/docs/documentation/doc-references/arkts-common-0000001774129201) +- [DataChangeListener](https://developer.harmonyos.com/cn/docs/documentation/doc-references/arkts-common-0000001774129201) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/implementation_not_allowed_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/implementation_not_allowed_errors.md new file mode 100644 index 0000000000..2504b8e9c1 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/implementation_not_allowed_errors.md @@ -0,0 +1,191 @@ +# 实现不允许错误 + +## 错误描述 + +在 ArkTS 中,UI 组件必须使用 `@Component` 装饰器装饰,并且必须在 `build()` 方法中返回 UI 结构。直接在文件中编写 UI 组件而不使用 `@Component` 装饰器会导致"实现不允许"错误。 + +## 错误示例 + +```typescript +// 错误:直接在文件中编写 UI 组件 +Row() { + Text('Hello') +} +``` + +**错误信息:** +``` +Implementation not allowed +``` + +## 解决方案 + +### 方案一:使用 @Component 装饰器 + +将 UI 组件包装在 `@Component` 装饰器中,并添加 `build()` 方法。 + +```typescript +@Component +struct MyComponent { + build() { + Row() { + Text('Hello') + } + } +} +``` + +### 方案二:使用 @Entry 装饰器(如果是页面入口) + +如果是页面入口组件,使用 `@Entry` 装饰器。 + +```typescript +@Entry +@Component +struct MyPage { + build() { + Row() { + Text('Hello') + } + } +} +``` + +## 简单示例 + +```typescript +@Entry +@Component +struct MyPage { + @State count: number = 0; + + build() { + Column() { + Text(`计数: ${this.count}`) + .fontSize(20) + .margin({ bottom: 16 }) + + Button('增加') + .onClick(() => { + this.count++; + }) + } + .padding(16) + } +} +``` + +## 详细代码示例 + +- [ImplementationNotAllowedError.ets](../assets/ImplementationNotAllowedError.ets) - 完整的实现不允许错误修复示例,包含正确的组件结构 + +## 最佳实践 + +1. **使用 @Component 装饰器**:所有自定义组件都必须使用 `@Component` 装饰器 +2. **使用 @Entry 装饰器**:页面入口组件必须使用 `@Entry` 装饰器 +3. **实现 build() 方法**:所有组件都必须实现 `build()` 方法并返回 UI 结构 +4. **遵循组件结构**:UI 组件必须遵循 ArkTS 的组件结构规范 + +## 常见错误 + +```typescript +// ❌ 错误:直接在文件中编写 UI 组件 +Row() { + Text('Hello') +} + +// ❌ 错误:缺少 @Component 装饰器 +struct MyComponent { + build() { + Row() { + Text('Hello') + } + } +} + +// ❌ 错误:缺少 build() 方法 +@Component +struct MyComponent { + // 缺少 build() 方法 +} + +// ✅ 正确:使用 @Component 装饰器和 build() 方法 +@Component +struct MyComponent { + build() { + Row() { + Text('Hello') + } + } +} + +// ✅ 正确:页面入口使用 @Entry 装饰器 +@Entry +@Component +struct MyPage { + build() { + Row() { + Text('Hello') + } + } +} +``` + +## 组件结构规范 + +### 基本组件结构 + +```typescript +@Component +struct MyComponent { + // 状态变量 + @State count: number = 0; + + // 私有属性 + private scroller: Scroller = new Scroller(); + + // 生命周期方法 + aboutToAppear() { + // 组件即将出现时调用 + } + + aboutToDisappear() { + // 组件即将消失时调用 + } + + // 自定义方法 + private handleClick() { + this.count++; + } + + // UI 构建方法 + build() { + Column() { + Text('Hello') + } + } +} +``` + +### 页面入口组件结构 + +```typescript +@Entry +@Component +struct MyPage { + // 状态变量 + @State title: string = 'My Page'; + + // 生命周期方法 + aboutToAppear() { + // 页面即将出现时调用 + } + + // UI 构建方法 + build() { + Column() { + Text(this.title) + } + } +} +``` diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/interface_method_signature_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/interface_method_signature_errors.md new file mode 100644 index 0000000000..ec268d4403 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/interface_method_signature_errors.md @@ -0,0 +1,143 @@ +# Interface Method Signature Errors + +## Error: Interface method signature mismatch + +### Error Message +``` +Object literal must correspond to some explicitly declared class or interface +``` + +### Cause +When implementing an interface with an object literal, method signatures in the object must match the interface exactly. Using method syntax (`method() {}`) instead of property syntax (`method: () => {}`) causes type errors. + +### Solution +Use property syntax for methods in object literals that implement interfaces. Change `method() {}` to `method: () => {}`. + +### Key Points +- Use property syntax for methods in object literals +- Change `method(): ReturnType {}` to `method: () => ReturnType {}` +- Ensure parameter types match to interface +- Ensure return types match to interface + +### Basic Pattern +```typescript +// ❌ Wrong: Method syntax in object literal +interface MyInterface { + updateWindowInfo(): void; + destroy(): void; +} + +const myObject: MyInterface = { + updateWindowInfo(): void { + console.log('Updated'); + }, + destroy(): void { + console.log('Destroyed'); + } +}; + +// ✅ Correct: Property syntax in object literal +interface MyInterface { + updateWindowInfo: () => void; + destroy: () => void; +} + +const myObject: MyInterface = { + updateWindowInfo: () => { + console.log('Updated'); + }, + destroy: () => { + console.log('Destroyed'); + } +}; +``` + +### Common Patterns +```typescript +// No parameters +interface SimpleInterface { + doSomething: () => void; +} + +const obj: SimpleInterface = { + doSomething: () => { + console.log('Doing something'); + } +}; + +// With parameters +interface ParameterInterface { + calculate: (a: number, b: number) => number; +} + +const obj: ParameterInterface = { + calculate: (a: number, b: number): number => { + return a + b; + } +}; + +// With optional parameters +interface OptionalInterface { + process: (data: string, options?: Options) => void; +} + +const obj: OptionalInterface = { + process: (data: string, options?: Options): void => { + console.log(data, options); + } +}; +``` + +### Detailed Examples +For more detailed code examples, see: +- [Interface Definition](../assets/InterfaceMethodSignatureError.ets#L1-L6) +- [Object Literal Implementation](../assets/InterfaceMethodSignatureError.ets#L8-L24) +- [Usage Pattern](../assets/InterfaceMethodSignatureError.ets#L26-L42) + +### Best Practices +1. **Use property syntax**: Always use `method: () => {}` syntax for object literals +2. **Match signatures**: Ensure method signatures match to interface exactly +3. **Add type annotations**: Add explicit parameter and return types +4. **Keep interfaces simple**: Avoid overly complex method signatures +5. **Document interfaces**: Add comments explaining interface purpose + +### Common Mistakes +```typescript +// ❌ Wrong: Method syntax in object literal +interface MyInterface { + updateWindowInfo(): void; +} + +const obj: MyInterface = { + updateWindowInfo(): void { + console.log('Updated'); + } +}; + +// ❌ Wrong: Method syntax in object literal +interface MyInterface { + updateWindowInfo: () => void; +} + +const obj: MyInterface = { + updateWindowInfo(): void { + console.log('Updated'); + } +}; + +// ✅ Correct: Property syntax in object literal +interface MyInterface { + updateWindowInfo: () => void; +} + +const obj: MyInterface = { + updateWindowInfo: () => { + console.log('Updated'); + } +}; +``` + +### Related Files +- [Code Example](../assets/InterfaceMethodSignatureError.ets) +- [Object Literal Interface Errors](./object_literal_interface_errors.md) +- [ArkTS Language Guide](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-get-started) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/notification_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/notification_errors.md new file mode 100644 index 0000000000..0ec13436a4 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/notification_errors.md @@ -0,0 +1,110 @@ +# Notification API Type Errors + +## Error: `notificationManager.ContentType` type incompatibility + +### Error Message +``` +Type 'notificationManager.ContentType' is not assignable to type 'notification.ContentType' +``` + +### Cause +Type mismatch between `notificationManager.ContentType` and `notification.ContentType`. The `notificationManager.ContentType` enum has additional properties that are not present in `notification.ContentType`, causing type incompatibility. + +### Solution +Cast the ContentType value to `number` type to resolve the type incompatibility. + +### Key Points +- Import from `@kit.NotificationKit`: `import { notificationManager } from '@kit.NotificationKit'` +- Cast to `number`: `as number` when assigning to `contentType` +- Use proper error handling with `BusinessError` +- Use `hilog` for logging instead of `console` + +### Notification Request Structure +```typescript +interface NotificationRequest { + id: number; + content: NotificationContent; + notificationSlotType?: SlotType; + isOngoing?: boolean; + isUnremovable?: boolean; + deliveryTime?: number; + tapDismissed?: boolean; + autoDeletedTime?: number; + classification?: Classification; + groupName?: string; + slotType?: SlotType; + ... +} +``` + +### Content Types +```typescript +notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT +notificationManager.ContentType.NOTIFICATION_CONTENT_LONG_TEXT +notificationManager.ContentType.NOTIFICATION_CONTENT_PICTURE +notificationManager.ContentType.NOTIFICATION_CONTENT_CONVERSATION +notificationManager.ContentType.NOTIFICATION_CONTENT_MULTILINE +notificationManager.ContentType.NOTIFICATION_CONTENT_MEDIA +notificationManager.ContentType.NOTIFICATION_CONTENT_LOCAL_LIVE_VIEW +notificationManager.ContentType.NOTIFICATION_CONTENT_LIVE_VIEW +``` + +### Basic Text Content +```typescript +interface NotificationBasicContent { + title: string; + text: string; + additionalText?: string; + largeIcon?: string; + briefText?: string; + expandedTitle?: string; +} +``` + +### Publishing Pattern +```typescript +let notificationRequest: notificationManager.NotificationRequest = { + id: 1, + content: { + contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT as number, + normal: { + title: 'Test Notification', + text: 'This is a test notification', + additionalText: 'Additional text' + } + } +}; + +notificationManager.publish(notificationRequest) + .then(() => { + hilog.info(0x0000, 'testTag', 'Publish notification success'); + }) + .catch((err: BusinessError) => { + hilog.error(0x0000, 'testTag', 'Publish notification failed: %{public}s', JSON.stringify(err)); + }); +``` + +### Error Handling +```typescript +// Common error codes +if (err.code === 1600001) { + // Notification not enabled +} else if (err.code === 1600002) { + // Too many notifications +} else if (err.code === 1600003) { + // Invalid notification +} else if (err.code === 1600004) { + // Notification slot not found +} +``` + +### Best Practices +1. **Cast to number**: Always cast ContentType to `number` to avoid type errors +2. **Use hilog**: Use `hilog` instead of `console` for better logging +3. **Handle errors**: Always catch and handle BusinessError +4. **Use unique IDs**: Ensure notification IDs are unique +5. **Test permissions**: Verify notification permissions are granted + +### Related Files +- [Code Example](../assets/NotificationError.ets) +- [Notification Kit Documentation](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-api-notificationmanager-V5) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/object_literal_interface_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/object_literal_interface_errors.md new file mode 100644 index 0000000000..bb88b58090 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/object_literal_interface_errors.md @@ -0,0 +1,156 @@ +# Object Literal Interface Errors + +## Error: `Object literal must correspond to some explicitly declared class or interface` + +### Error Message +``` +Object literal must correspond to some explicitly declared class or interface +``` + +### Cause +ArkTS has strict type checking for object literals. When you create an object literal, it must match an explicitly declared interface or class. This prevents type errors and ensures type safety. + +### Solution +Define an interface or class that describes the shape of the object literal, then use that type for the object. + +### Key Points +- Define interfaces for object shapes before using them +- Use the interface type for arrays of objects +- Ensure object literals match the interface exactly +- Use optional properties (`?`) for fields that may not be present + +### Basic Pattern +```typescript +interface Article { + title: string; + desc: string; + image: Resource; +} + +@Entry +@Component +struct MyComponent { + private articles: Article[] = [ + { title: '文章1', desc: '这是文章1的描述', image: $r('app.media.article1') }, + { title: '文章2', desc: '这是文章2的描述', image: $r('app.media.article2') } + ]; + + build() { + ForEach(this.articles, (article: Article) => { + Text(article.title) + }) + } +} +``` + +### Interface Definition +```typescript +// Basic interface +interface User { + id: number; + name: string; + email: string; +} + +// Interface with optional properties +interface Config { + width: number; + height: number; + color?: string; + opacity?: number; +} + +// Interface with nested objects +interface Article { + title: string; + desc: string; + image: Resource; + metadata?: { + author: string; + date: string; + }; +} +``` + +### Common Patterns +```typescript +// Configuration object +interface Config { + width: number; + height: number; + color: string; +} + +const config: Config = { width: 100, height: 100, color: 'red' }; + +// User data +interface User { + id: number; + name: string; + email?: string; +} + +const user: User = { id: 1, name: 'John' }; + +// API response +interface ApiResponse { + success: boolean; + data: any; + message?: string; +} + +const response: ApiResponse = { success: true, data: {} }; + +// Breakpoint configuration +interface Breakpoint { + name: string; + range: [number, number]; +} + +const breakpoints: Breakpoint[] = [ + { name: 'sm', range: [320, 599] }, + { name: 'md', range: [600, 839] } +]; +``` + +### Detailed Examples +For more detailed code examples, see: +- [Interface Definition](../assets/ObjectLiteralInterfaceError.ets#L1-L6) +- [Array of Objects](../assets/ObjectLiteralInterfaceError.ets#L8-L14) +- [ForEach Usage](../assets/ObjectLiteralInterfaceError.ets#L16-L48) + +### Best Practices +1. **Define interfaces first**: Always define interfaces before using object literals +2. **Use descriptive names**: Interface names should clearly describe the object shape +3. **Make properties optional**: Use `?` for properties that may not be present +4. **Reuse interfaces**: Define interfaces once and reuse them throughout your code +5. **Document interfaces**: Add comments explaining the purpose of each interface + +### Common Mistakes +```typescript +// ❌ Wrong: Object literal without interface +const articles = [ + { title: '文章1', desc: '描述', image: $r('app.media.icon') } +]; + +// ❌ Wrong: Array type with object literal +const articles: Array<{ title: string, desc: string, image: Resource }> = [ + { title: '文章1', desc: '描述', image: $r('app.media.icon') } +]; + +// ✅ Correct: Define interface first +interface Article { + title: string; + desc: string; + image: Resource; +} + +const articles: Article[] = [ + { title: '文章1', desc: '描述', image: $r('app.media.icon') } +]; +``` + +### Related Files +- [Code Example](../assets/ObjectLiteralInterfaceError.ets) +- [Object Spread Type Errors](./object_spread_errors.md) +- [ArkTS Language Guide](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-get-started) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/object_literal_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/object_literal_type_errors.md new file mode 100644 index 0000000000..40d76df3c3 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/object_literal_type_errors.md @@ -0,0 +1,110 @@ +# Object Literal Type Declaration Error + +## 错误描述 + +在 ArkTS 中,不能使用对象字面量作为类型声明。必须使用 `interface` 或 `class` 来定义类型。 + +## 错误示例 + +```typescript +function getWindowSize(): { width: number; height: number } { + return { width: 100, height: 200 }; +} +``` + +**错误信息:** +``` +Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types) +``` + +## 解决方案 + +### 方案1:使用 interface(推荐) + +```typescript +interface WindowSize { + width: number; + height: number; +} + +function getWindowSize(): WindowSize { + return { width: 100, height: 200 }; +} +``` + +### 方案2:使用 type 别名 + +```typescript +type WindowSize = { + width: number; + height: number; +}; + +function getWindowSize(): WindowSize { + return { width: 100, height: 200 }; +} +``` + +### 方案3:使用 class + +```typescript +class WindowSize { + width: number = 0; + height: number = 0; +} + +function getWindowSize(): WindowSize { + const size = new WindowSize(); + size.width = 100; + size.height = 200; + return size; +} +``` + +## 详细说明 + +ArkTS 禁止使用对象字面量作为类型声明,这是为了: + +1. **提高代码可读性**:使用命名的类型更清晰 +2. **支持类型复用**:interface 和 type 可以在多个地方使用 +3. **增强类型检查**:明确的类型定义可以提供更好的类型推断 + +## 简单示例 + +```typescript +interface WindowSize { + width: number; + height: number; +} + +@Entry +@Component +struct WindowSizeExample { + @State windowSize: WindowSize = { width: 0, height: 0 }; + + aboutToAppear() { + this.windowSize = this.getWindowSize(); + } + + private getWindowSize(): WindowSize { + const windowStage = this.getUIContext().getHostContext() as common.UIAbilityContext; + const windowClass = windowStage.getMainWindowSync(); + const windowProperties = windowClass.getWindowProperties(); + return { + width: windowProperties.windowRect.width, + height: windowProperties.windowRect.height + }; + } + + build() { + Column() { + Text(`宽度: ${this.windowSize.width}`) + Text(`高度: ${this.windowSize.height}`) + } + } +} +``` + +## 详细代码示例 + +> [ObjectLiteralTypeError.ets](../assets/ObjectLiteralTypeError.ets) - 完整的对象字面量类型声明错误示例和修复方案 diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/object_spread_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/object_spread_errors.md new file mode 100644 index 0000000000..446401e9fe --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/object_spread_errors.md @@ -0,0 +1,105 @@ +# Object Spread Type Errors + +## Error: Object spread type inference issue + +### Error Message +``` +Type inference errors when using object spread +``` + +### Cause +ArkTS has strict type inference rules for object spread operations. Without explicit type annotations, the compiler cannot properly infer the resulting type. + +### Solution +Explicitly type objects or use proper interface definitions before using spread operations. + +### Key Points +- Define interfaces for object shapes +- Use explicit type annotations for spread results +- Ensure spread and target types are compatible +- Use optional properties for partial updates + +### Interface Definition +```typescript +interface Config { + width: number; + height: number; + color?: string; + opacity?: number; +} +``` + +### Spread Pattern +```typescript +const baseConfig: Config = { width: 100, height: 100 }; +const newConfig: Config = { ...baseConfig, color: 'red' }; +``` + +### Partial Updates +```typescript +interface User { + id: number; + name: string; + email?: string; + phone?: string; +} + +const baseUser: User = { id: 1, name: 'John' }; +const updatedUser: User = { + ...baseUser, + email: 'john@example.com' +}; +``` + +### Nested Objects +```typescript +interface NestedConfig { + layout: { + width: number; + height: number; + }; + style: { + color: string; + opacity: number; + }; +} + +const base: NestedConfig = { + layout: { width: 100, height: 100 }, + style: { color: 'red', opacity: 1.0 } +}; + +const updated: NestedConfig = { + ...base, + layout: { ...base.layout, width: 200 } +}; +``` + +### Common Patterns +```typescript +// Configuration update +const config: Config = { ...defaultConfig, ...userConfig }; + +// State update +const newState: State = { ...currentState, ...updates }; + +// Merge defaults +const merged: Options = { ...defaultOptions, ...userOptions }; + +// Conditional spread +const result: Result = { + ...base, + ...(condition ? { extra: 'value' } : {}) +}; +``` + +### Best Practices +1. **Define interfaces**: Always define interfaces for object shapes +2. **Use explicit types**: Annotate spread results with interface types +3. **Optional properties**: Use optional properties for partial updates +4. **Type compatibility**: Ensure spread and target types are compatible +5. **Avoid deep spread**: Be careful with nested object spreads + +### Related Files +- [Code Example](../assets/ObjectSpreadError.ets) +- [ArkTS Language Guide](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-get-started) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/possibly_null_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/possibly_null_errors.md new file mode 100644 index 0000000000..d2037605ab --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/possibly_null_errors.md @@ -0,0 +1,156 @@ +# 对象可能为 null 错误 + +## 错误描述 + +在 ArkTS 中,编译器会严格检查可能为 null 的对象访问。如果对象可能为 null,但直接访问其属性,会导致"Object is possibly 'null'"错误。 + +### 错误信息 + +``` +Object is possibly 'null' +``` + +或 + +``` +Variable 'xxx' is possibly null +``` + +## 错误示例 + +```typescript +// ❌ 错误:对象可能为 null +let display = display.getDefaultDisplaySync(); +console.log(display.width); // 错误:Object is possibly 'null' + +// ❌ 错误:条件判断不完整 +if (display) { + console.log(display.width); // 某些情况下仍可能为 null +} +``` + +## 解决方案 + +### 方案一:使用 !== null 检查 + +显式检查对象不等于 null: + +```typescript +// ✅ 正确:使用 !== null 检查 +let display = display.getDefaultDisplaySync(); +if (display !== null) { + console.log(display.width); +} +``` + +### 方案二:使用可选链和空值合并 + +```typescript +// ✅ 正确:使用可选链和空值合并 +let display = display.getDefaultDisplaySync(); +let width = display?.width ?? 0; +``` + +### 方案三:使用 let 声明可空类型 + +```typescript +// ✅ 正确:显式声明可空类型 +let display: Display | null = display.getDefaultDisplaySync(); +if (display !== null) { + console.log(display.width); +} +``` + +### 方案四:非空断言(谨慎使用) + +```typescript +// ✅ 正确(但需确保不会为 null):使用非空断言 +let display = display.getDefaultDisplaySync()!; +console.log(display.width); +``` + +## 简单示例 + +```typescript +import { display } from '@kit.ArkUI'; + +@Entry +@Component +struct NullCheckExample { + private myDisplay: Display | null = null; + + aboutToAppear() { + this.myDisplay = display.getDefaultDisplaySync(); + } + + build() { + Column() { + if (this.myDisplay !== null) { + Text(`Width: ${this.myDisplay.width}`) + .fontSize(24) + } + } + .width('100%') + } +} +``` + +## 详细代码示例 + +- [PossiblyNullError.ets](../assets/PossiblyNullError.ets) - 完整的 null 检查错误修复示例 + +## 最佳实践 + +1. **优先使用 !== null 检查**:最安全的方式,显式检查对象不为 null +2. **使用可选链**:`?.` 可以在对象为 null 时返回 undefined +3. **使用空值合并**:`??` 提供默认值 +4. **避免非空断言**:除非确定对象不会为 null,否则不要使用 `!` +5. **类型注解**:对可能为 null 的变量显式声明联合类型 + +## 常见场景 + +### Display API + +```typescript +// ❌ 错误 +let display = display.getDefaultDisplaySync(); +let width = display.width; + +// ✅ 正确 +let display = display.getDefaultDisplaySync(); +if (display !== null) { + let width = display.width; +} +``` + +### Window API + +```typescript +// ❌ 错误 +let window = window.getLastWindow(context); +window.setFullScreen(true); + +// ✅ 正确 +let window = await window.getLastWindow(context); +if (window !== null) { + await window.setFullScreen(true); +} +``` + +### 可选属性 + +```typescript +// ❌ 错误 +let config = { width: 100 }; +let w = config.height; // height 不存在 + +// ✅ 正确 +interface Config { + width: number; + height?: number; +} +let config: Config = { width: 100 }; +if (config.height !== undefined) { + let h = config.height; +} +``` diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/resource_conversion_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/resource_conversion_errors.md new file mode 100644 index 0000000000..10f68f1226 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/resource_conversion_errors.md @@ -0,0 +1,154 @@ +# Resource 类型转换错误 + +## 错误描述 + +在 ArkTS 中,不能将 `Resource` 类型直接转换为 `string` 或 `number` 类型。Resource 是一个特殊的资源引用类型,需要通过特定的方式使用。 + +## 错误示例 + +```typescript +const message: string = $r('app.string.hello'); +const fontSize: number = $r('app.float.title_font_size'); +``` + +**错误信息:** +``` +Conversion of type 'Resource' to type 'string'/'number' may be a mistake because neither type sufficiently overlaps with the other. +``` + +## 解决方案 + +### 方案1:直接在 UI 组件中使用(推荐) + +Resource 类型可以直接作为属性值传递给 UI 组件,系统会自动处理资源解析。 + +```typescript +@Entry +@Component +struct MyComponent { + build() { + Column() { + Text($r('app.string.hello')) + .fontSize($r('app.float.title_font_size')) + .width($r('app.float.layout_width')) + .height($r('app.float.layout_height')) + .backgroundColor($r('app.color.background_color')) + } + } +} +``` + +### 方案2:使用资源管理器获取字符串值 + +如果需要获取字符串的实际值,可以使用 `ResourceManager`。 + +```typescript +import { resourceManager } from '@kit.LocalizationKit'; + +async function getStringResource(context: Context, resourceId: number): Promise { + const manager = context.resourceManager; + return await manager.getString(resourceId); +} +``` + +### 方案3:使用 getNumber 获取数值资源 + +对于数值资源,使用 `getNumber` 方法。 + +```typescript +import { resourceManager } from '@kit.LocalizationKit'; + +async function getNumberResource(context: Context, resourceId: number): Promise { + const manager = context.resourceManager; + return await manager.getNumber(resourceId); +} +``` + +### 方案4:使用 getStringByName 根据名称获取 + +根据资源名称获取字符串值。 + +```typescript +import { resourceManager } from '@kit.LocalizationKit'; + +async function getStringByName(context: Context, name: string): Promise { + const manager = context.resourceManager; + return await manager.getStringByName(name); +} +``` + +## 详细说明 + +Resource 类型的特点: + +1. **延迟加载**:资源在需要时才被解析 +2. **多语言支持**:根据系统语言自动选择对应的资源 +3. **主题适配**:支持深色/浅色主题切换 +4. **类型安全**:编译时检查资源引用的正确性 + +## 资源类型对照表 + +| 资源类型 | $r 语法 | 资源文件位置 | 示例 | +|---------|---------|-------------|------| +| 字符串 | `$r('app.string.name')` | `resources/base/element/string.json` | `$r('app.string.hello')` | +| 颜色 | `$r('app.color.name')` | `resources/base/element/color.json` | `$r('app.color.primary')` | +| 浮点数 | `$r('app.float.name')` | `resources/base/element/float.json` | `$r('app.float.title_font_size')` | +| 整数 | `$r('app.integer.name')` | `resources/base/element/integer.json` | `$r('app.integer.max_count')` | +| 布尔 | `$r('app.boolean.name')` | `resources/base/element/boolean.json` | `$r('app.boolean.is_enabled')` | +| 媒体 | `$r('app.media.name')` | `resources/base/media/` | `$r('app.media.icon')` | + +## 简单示例 + +```typescript +@Entry +@Component +struct ResourceUsageExample { + @State displayText: string = ''; + + async loadStringResource() { + try { + const context = this.getUIContext().getHostContext(); + const manager = context.resourceManager; + this.displayText = await manager.getString($r('app.string.hello').id); + } catch (err) { + console.error(`Failed to load string resource: ${JSON.stringify(err)}`); + } + } + + build() { + Column() { + Text('Resource 类型使用示例') + .fontSize($r('app.float.title_font_size')) + .fontWeight(FontWeight.Bold) + .margin(20) + + Text($r('app.string.hello')) + .fontSize($r('app.float.content_font_size')) + .width($r('app.float.layout_width')) + .padding($r('app.float.padding')) + .backgroundColor($r('app.color.background_color')) + .borderRadius($r('app.float.border_radius')) + .margin(20) + + Button('加载字符串资源') + .onClick(() => { + this.loadStringResource(); + }) + .margin(20) + + if (this.displayText) { + Text(`加载的字符串: ${this.displayText}`) + .fontSize(14) + .margin(20) + } + } + .width('100%') + .height('100%') + .padding(20) + } +} +``` + +## 详细代码示例 + +> [ResourceConversionError.ets](../assets/ResourceConversionError.ets) - 完整的 Resource 类型转换错误示例和修复方案 diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/standalone_function_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/standalone_function_errors.md new file mode 100644 index 0000000000..054079d1de --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/standalone_function_errors.md @@ -0,0 +1,68 @@ +# Standalone Function `this` Usage Error + +## 错误描述 + +在独立函数(非类方法)中不能直接使用 `this`,因为 `this` 在独立函数中没有上下文绑定。 + +## 错误示例 + +```typescript +async function getAvoidArea() { + const win = await window.getLastWindow(this.getUIContext().getHostContext()); + return win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); +} +``` + +**错误信息:** +``` +Cannot find name 'this' +``` + +## 解决方案 + +将上下文作为参数传递给独立函数。 + +```typescript +async function getAvoidArea(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(err); + return; + } + const avoidArea = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); + resolve(avoidArea); + }); + }); +} +``` + +## 简单示例 + +```typescript +@Entry +@Component +struct Example { + @State avoidAreaHeight: number = 0; + + async aboutToAppear() { + const context = this.getUIContext().getHostContext() as common.UIAbilityContext; + try { + const avoidArea = await getAvoidArea(context); + this.avoidAreaHeight = avoidArea.topRect.height; + } catch (err) { + console.error('获取避让区域失败:', err); + } + } + + build() { + Column() { + Text(`避让区域高度: ${this.avoidAreaHeight}`) + } + } +} +``` + +## 详细代码示例 + +- [StandaloneFunctionError.ets](../assets/StandaloneFunctionError.ets) - 完整的独立函数上下文传递示例,包含多个独立函数的使用 diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/storage_link_default_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/storage_link_default_errors.md new file mode 100644 index 0000000000..97c8f8ae16 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/storage_link_default_errors.md @@ -0,0 +1,124 @@ +# @StorageLink Default Value Errors + +## Error: `The '@StorageLink' property must be specified a default value` + +### Error Message +``` +The '@StorageLink' property must be specified a default value +``` + +### Cause +ArkTS requires all `@StorageLink` decorated properties to have a default value. This is a strict type safety requirement to ensure the property always has a valid initial state. + +### Solution +Add a default value to the `@StorageLink` property, typically `= undefined` for optional types or a specific default value for required types. + +### Key Points +- Always provide a default value for `@StorageLink` properties +- Use `= undefined` for optional types +- Use specific default values for required types (e.g., `= 0`, `= ''`, `= false`) +- Initialize the actual value in `aboutToAppear()` using `AppStorage.setOrCreate()` + +### Basic Pattern +```typescript +@Entry +@Component +struct MyComponent { + @StorageLink('myKey') myValue: number = 0; + + aboutToAppear() { + AppStorage.setOrCreate('myKey', 0); + } + + build() { + Text(`Value: ${this.myValue}`) + } +} +``` + +### Optional Type Pattern +```typescript +interface WindowUtil { + width: number; + height: number; + density: number; + updateWindowInfo: () => void; + destroy: () => void; +} + +@Entry +@Component +struct MyComponent { + @StorageLink('windowUtil') windowUtil?: WindowUtil = undefined; + + aboutToAppear() { + AppStorage.setOrCreate('windowUtil', { + width: 0, + height: 0, + density: 1.0, + updateWindowInfo: () => {}, + destroy: () => {} + }); + } + + build() { + Text(`Width: ${this.windowUtil?.width || 0}`) + } +} +``` + +### Common Default Values +```typescript +// Number types +@StorageLink('counter') count: number = 0; +@StorageLink('width') width: number = 100; +@StorageLink('opacity') opacity: number = 1.0; + +// String types +@StorageLink('userName') userName: string = ''; +@StorageLink('title') title: string = 'Default Title'; + +// Boolean types +@StorageLink('isDarkMode') isDarkMode: boolean = false; +@StorageLink('isLoading') isLoading: boolean = true; + +// Array types +@StorageLink('items') items: Array = []; +@StorageLink('numbers') numbers: number[] = [1, 2, 3]; + +// Optional types +@StorageLink('user') user?: User = undefined; +@StorageLink('settings') settings?: Settings = undefined; +``` + +### Detailed Examples +For more detailed code examples, see: +- [Optional Type Pattern](../assets/StorageLinkDefaultError.ets#L8-L31) +- [Initialization Pattern](../assets/StorageLinkDefaultError.ets#L11-L23) +- [Usage Pattern](../assets/StorageLinkDefaultError.ets#L25-L39) + +### Best Practices +1. **Always provide default value**: Never leave `@StorageLink` without a default +2. **Use appropriate defaults**: Choose defaults that make sense for your use case +3. **Initialize in aboutToAppear**: Set the actual value when component appears +4. **Use optional types carefully**: Only use `?` when the value can legitimately be undefined +5. **Document default values**: Add comments explaining why a specific default was chosen + +### Common Mistakes +```typescript +// ❌ Wrong: No default value +@StorageLink('myValue') myValue: number; + +// ❌ Wrong: Using null instead of undefined +@StorageLink('myValue') myValue: number = null; + +// ✅ Correct: With default value +@StorageLink('myValue') myValue: number = 0; + +// ✅ Correct: Optional type with undefined +@StorageLink('myValue') myValue?: number = undefined; +``` + +### Related Files +- [Code Example](../assets/StorageLinkDefaultError.ets) +- [AppStorage Type Errors](./appstorage_errors.md) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/title_button_rect_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/title_button_rect_type_errors.md new file mode 100644 index 0000000000..0db0c9d365 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/title_button_rect_type_errors.md @@ -0,0 +1,167 @@ +# TitleButtonRect Type Error + +## 错误描述 + +`window.getTitleButtonRect()` 方法返回的是 `window.TitleButtonRect` 类型,而不是 `window.Rect` 类型。如果函数返回类型声明为 `window.Rect`,会导致类型不匹配错误。 + +## 错误信息 + +### 错误 1: 类型不匹配 +``` +Argument of type 'TitleButtonRect' is not assignable to parameter of type 'Rect | PromiseLike'. + Property 'left' is missing in type 'TitleButtonRect' but required in type 'Rect'. +``` + +### 错误 2: 访问不存在的属性 +``` +Property 'left' does not exist on type 'TitleButtonRect'. +Property 'top' does not exist on type 'TitleButtonRect'. +``` + +**原因**:`TitleButtonRect` 类型只包含 `width` 和 `height` 属性,不包含 `left` 和 `top` 属性。 + +## 错误示例 + +```typescript +async function getTitleButtonRect(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + const titleButtonRect = win.getTitleButtonRect(); + resolve(titleButtonRect); + }); + }); +} +``` + +## 解决方案 + +将函数返回类型从 `window.Rect` 改为 `window.TitleButtonRect`。 + +```typescript +async function getTitleButtonRect(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + const titleButtonRect = win.getTitleButtonRect(); + resolve(titleButtonRect); + }); + }); +} +``` + +## 类型说明 + +`window.TitleButtonRect` 和 `window.Rect` 是两个不同的类型: + +- `window.Rect`: 包含 `left`, `top`, `width`, `height` 属性 +- `window.TitleButtonRect`: **只包含** `width`, `height` 属性(不包含 `left` 和 `top` 属性) + +**重要提示**:`TitleButtonRect` 类型只提供宽度和高度信息,不包含位置信息(left 和 top)。如果需要位置信息,需要使用其他 API 获取。 + +## 简单示例 + +```typescript +import { window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + +async function getTitleButtonRect(context: common.UIAbilityContext): Promise { + return new Promise((resolve, reject) => { + window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + reject(new Error(err.message)); + return; + } + const titleButtonRect = win.getTitleButtonRect(); + resolve(titleButtonRect); + }); + }); +} + +@Entry +@Component +struct TitleButtonRectExample { + @State titleBarHeight: number = 0; + @State titleBarWidth: number = 0; + + async aboutToAppear() { + const context = this.getUIContext().getHostContext() as common.UIAbilityContext; + try { + const titleButtonRect = await getTitleButtonRect(context); + // TitleButtonRect 只包含 width 和 height 属性 + this.titleBarHeight = titleButtonRect.height; + this.titleBarWidth = titleButtonRect.width; + // ❌ 错误:不能访问 left 和 top 属性 + // this.titleBarLeft = titleButtonRect.left; // Property 'left' does not exist + // this.titleBarTop = titleButtonRect.top; // Property 'top' does not exist + } catch (err) { + console.error('获取标题栏按钮区域失败:', err instanceof Error ? err.message : String(err)); + } + } + + build() { + Column() { + Text(`标题栏高度: ${this.titleBarHeight}`) + Text(`标题栏宽度: ${this.titleBarWidth}`) + } + } +} +``` + +## 详细代码示例 + +- [TitleButtonRectTypeError.ets](../assets/TitleButtonRectTypeError.ets) - TitleButtonRect 类型错误的完整示例,包含错误和正确的解决方案 +- [StandaloneFunctionContext.ets](../assets/StandaloneFunctionContext.ets#L34-L47) - 完整的 TitleButtonRect 类型使用示例,包含错误处理 +- [StandaloneFunctionError.ets](../assets/StandaloneFunctionError.ets#L34-L47) - 独立函数中的 TitleButtonRect 类型使用示例 + +## 相关类型 + +| 类型 | 说明 | 用途 | +|------|------|------| +| `window.Rect` | 通用矩形区域 | 窗口区域、避让区域等 | +| `window.TitleButtonRect` | 标题栏按钮区域 | 标题栏按钮的位置和大小 | +| `window.AvoidArea` | 避让区域 | 系统栏、导航栏等避让区域 | + +## 最佳实践 + +1. **使用正确的返回类型**: 根据实际调用的 API 返回类型来声明函数返回类型 +2. **查看 API 文档**: 使用窗口 API 时,仔细查看返回值类型和可用属性 +3. **了解类型差异**: `TitleButtonRect` 只包含 `width` 和 `height`,不包含位置信息 +4. **类型转换**: 如果需要在不同类型之间转换,创建新的对象而不是直接赋值 +5. **避免访问不存在的属性**: 不要尝试访问 `TitleButtonRect` 的 `left` 和 `top` 属性 +6. **使用 TypeScript 类型推断**: 在某些情况下,可以省略返回类型注解让编译器推断 + +## 常见错误 + +```typescript +// ❌ 错误:返回类型声明为 Rect +async function getTitleButtonRect(): Promise { + const win = await window.getLastWindow(context); + return win.getTitleButtonRect(); +} + +// ✅ 正确:返回类型声明为 TitleButtonRect +async function getTitleButtonRect(): Promise { + const win = await window.getLastWindow(context); + return win.getTitleButtonRect(); +} + +// ✅ 正确:如果需要 Rect 类型,进行类型转换 +// 注意:TitleButtonRect 不包含 left 和 top 属性,只能提供 width 和 height +async function getTitleButtonRectAsRect(): Promise { + const win = await window.getLastWindow(context); + const titleButtonRect = win.getTitleButtonRect(); + return { + left: 0, // TitleButtonRect 不提供位置信息,需要从其他 API 获取 + top: 0, // TitleButtonRect 不提供位置信息,需要从其他 API 获取 + width: titleButtonRect.width, + height: titleButtonRect.height + }; +} +``` diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/unused_variable_warnings.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/unused_variable_warnings.md new file mode 100644 index 0000000000..a2703aa8cf --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/unused_variable_warnings.md @@ -0,0 +1,295 @@ +# 未使用变量警告解决方案 + +## 问题描述 + +在 HarmonyOS ArkUI 开发中,当函数参数或变量被声明但未使用时,会触发编译警告: + +``` +'scrollState' is declared but its value is never read. +``` + +这个警告提示代码中存在未使用的变量,可能是遗漏了某些逻辑或者参数确实不需要。 + +## 问题示例 + +```typescript +// ❌ 警告:scrollState 参数未使用 +.onDidScroll((scrollOffset: number, scrollState: ScrollState) => { + this.scrollOffset = scrollOffset; +}) +``` + +## 推荐解决方案 + +### 方案 1: 使用下划线前缀标记未使用参数 + +```typescript +// ✅ 推荐:使用下划线前缀 +.onDidScroll((scrollOffset: number, _scrollState: ScrollState) => { + this.scrollOffset = scrollOffset; +}) +``` + +### 方案 2: 使用该参数 + +```typescript +// ✅ 可选:如果需要使用该参数 +.onDidScroll((scrollOffset: number, scrollState: ScrollState) => { + this.scrollOffset = scrollOffset; + this.scrollState = scrollState; +}) +``` + +### 方案 3: 删除未使用的参数 + +```typescript +// ✅ 可选:如果确实不需要该参数 +.onDidScroll((scrollOffset: number) => { + this.scrollOffset = scrollOffset; +}) +``` + +## 下划线前缀约定 + +在 TypeScript/ArkTS 中,使用下划线 `_` 作为变量名前缀是一种常见的约定,表示该变量是故意未使用的: + +```typescript +// 明确表示 scrollState 参数是故意未使用的 +function example(scrollOffset: number, _scrollState: ScrollState) { + console.log(scrollOffset); + // scrollState 不会被使用,但保留参数以保持 API 一致性 +} +``` + +## 迁移步骤 + +### 1. 识别未使用变量警告 + +```typescript +// 查找编译警告中提到的变量 +.onDidScroll((scrollOffset: number, scrollState: ScrollState) => { + this.scrollOffset = scrollOffset; +}) +``` + +### 2. 添加下划线前缀 + +```typescript +// 为未使用的参数添加下划线前缀 +.onDidScroll((scrollOffset: number, _scrollState: ScrollState) => { + this.scrollOffset = scrollOffset; +}) +``` + +### 3. 或者使用该参数 + +```typescript +// 如果确实需要使用该参数 +.onDidScroll((scrollOffset: number, scrollState: ScrollState) => { + this.scrollOffset = scrollOffset; + this.scrollState = scrollState; +}) +``` + +## 完整示例 + +```typescript +@Entry +@Component +struct ScrollExample { + @State scrollOffset: number = 0; + @State scrollState: ScrollState = ScrollState.Idle; + private scroller: Scroller = new Scroller(); + + build() { + Column() { + Text(`滚动偏移: ${this.scrollOffset}`) + .fontSize(16) + .margin({ bottom: 8 }) + + Text(`滚动状态: ${this.getScrollStateName(this.scrollState)}`) + .fontSize(16) + .margin({ bottom: 16 }) + + Scroll(this.scroller) { + Column() { + ForEach(Array.from({ length: 50 }), (_: Object, index: number) => { + Text(`Item ${index + 1}`) + .fontSize(16) + .padding(12) + .margin({ bottom: 8 }) + .backgroundColor('#F5F5F5') + .borderRadius(4) + }, (_: Object, index: number) => `${index}`) + } + .width('100%') + } + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.Auto) + .onDidScroll((scrollOffset: number, _scrollState: ScrollState) => { + // 使用下划线前缀标记未使用的 scrollState 参数 + this.scrollOffset = scrollOffset; + }) + .onDidScroll((scrollOffset: number, scrollState: ScrollState) => { + // 在需要时使用 scrollState 参数 + this.scrollOffset = scrollOffset; + this.scrollState = scrollState; + }) + } + .width('100%') + .height('100%') + .padding(16) + } + + private getScrollStateName(state: ScrollState): string { + switch (state) { + case ScrollState.Idle: + return 'Idle'; + case ScrollState.Scroll: + return 'Scroll'; + case ScrollState.Fling: + return 'Fling'; + default: + return 'Unknown'; + } + } +} +``` + +> [查看完整示例](../assets/UnusedVariableWarning.ets) + +## 使用场景 + +### 1. 事件回调中的未使用参数 + +```typescript +// ✅ 使用下划线前缀 +.onClick((_event: ClickEvent) => { + this.handleClick(); +}) + +.onDidScroll((scrollOffset: number, _scrollState: ScrollState) => { + this.scrollOffset = scrollOffset; +}) +``` + +### 2. ForEach 中的未使用参数 + +```typescript +// ✅ 使用下划线前缀 +ForEach(this.items, (_item: Item, index: number) => { + Text(`Item ${index}`) +}, (item: Item, index: number) => `${index}`) +``` + +### 3. 函数参数中的未使用参数 + +```typescript +// ✅ 使用下划线前缀 +private processData(data: string, _options: ProcessOptions) { + return data.toUpperCase(); +} +``` + +### 4. 解构赋值中的未使用属性 + +```typescript +// ✅ 使用下划线前缀 +const { name, _id, _timestamp } = this.userData; +console.log(name); +``` + +## 最佳实践 + +### 1. 使用下划线前缀明确意图 + +```typescript +// 明确表示该参数是故意未使用的 +function example(required: string, _optional: string) { + console.log(required); +} +``` + +### 2. 保持 API 一致性 + +```typescript +// 即使某些参数未使用,也保留它们以保持 API 一致性 +.onDidScroll((scrollOffset: number, _scrollState: ScrollState) => { + this.scrollOffset = scrollOffset; +}) +``` + +### 3. 考虑是否真的不需要 + +```typescript +// 在添加下划线前缀前,考虑是否真的不需要该参数 +.onDidScroll((scrollOffset: number, scrollState: ScrollState) => { + this.scrollOffset = scrollOffset; + // 也许未来会需要 scrollState? + this.scrollState = scrollState; +}) +``` + +### 4. 删除真正未使用的变量 + +```typescript +// 如果变量确实不需要,直接删除 +// ❌ 不好:保留未使用的变量 +const unused = calculateSomething(); +doSomething(); + +// ✅ 好:删除未使用的变量 +doSomething(); +``` + +## 注意事项 + +1. **下划线前缀约定**:使用 `_` 作为前缀是 TypeScript 社区的常见约定 + +2. **明确意图**:下划线前缀明确表示该变量是故意未使用的 + +3. **保持一致性**:在项目中统一使用下划线前缀标记未使用变量 + +4. **定期清理**:定期检查并删除真正未使用的变量和参数 + +5. **代码审查**:在代码审查时关注未使用变量警告 + +## 相关 API 参考 + +- [TypeScript 未使用变量警告](https://www.typescriptlang.org/docs/handbook/compiler-options.html#noUnusedLocals) +- [ArkTS 编译选项](https://developer.harmonyos.com/cn/docs/documentation/guides/arkts-getting-started) + +## 迁移检查清单 + +- [ ] 识别所有未使用变量警告 +- [ ] 为确实不需要使用的参数添加下划线前缀 +- [ ] 考虑是否应该使用某些被标记为未使用的参数 +- [ ] 删除真正未使用的变量 +- [ ] 测试功能是否正常 +- [ ] 确认警告已消失 + +## 常见问题 + +### Q: 为什么要使用下划线前缀而不是删除参数? + +A: 有时需要保留参数以保持 API 一致性(如事件回调),下划线前缀明确表示这是故意的。 + +### Q: 下划线前缀会影响运行时行为吗? + +A: 不会。下划线前缀只是一个命名约定,不会影响代码的执行。 + +### Q: 可以在所有未使用变量上使用下划线前缀吗? + +A: 可以,但对于真正未使用的变量,建议删除而不是添加下划线前缀。 + +### Q: 如何区分故意未使用和遗漏使用? + +A: 如果参数是 API 的一部分(如事件回调),使用下划线前缀;如果是临时变量,考虑删除。 + +### Q: 编译器会自动处理未使用变量吗? + +A: 某些编译器选项会自动删除未使用的变量,但建议手动处理以保持代码清晰。 + +### Q: ForEach 中的未使用参数如何处理? + +A: 使用下划线前缀标记未使用的参数,如 `(_item: Item, index: number)`。 diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/utility_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/utility_type_errors.md new file mode 100644 index 0000000000..fc0518c54e --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/utility_type_errors.md @@ -0,0 +1,290 @@ +# Utility Type Errors + +## Error: Utility types not supported + +### Error Message +``` +Some of utility types are not supported (arkts-no-utility-types) +``` + +### Cause +ArkTS does not support TypeScript's utility types like `Parameters`, `ReturnType`, `Partial`, etc. These utility types are part of TypeScript's advanced type system but are not compatible with ArkTS's stricter type system. + +### Solution +Use explicit type definitions, interfaces, or type aliases instead of utility types. For function parameters, use `Object[]` or define explicit parameter types. + +### Key Points +- ArkTS does not support TypeScript utility types +- Use explicit type definitions instead +- Use interfaces or type aliases for complex types +- Use `Object[]` for rest parameters +- Define function types explicitly + +### ❌ Wrong Usage +```typescript +// ❌ Wrong: Using Parameters +function debounce void>(func: T, delay: number): T { + return ((...args: Parameters) => { + setTimeout(() => func(...args), delay); + }) as T; +} + +// ❌ Wrong: Using ReturnType +type Handler = () => string; +type Result = ReturnType; + +// ❌ Wrong: Using Partial +interface Config { + width: number; + height: number; + color?: string; +} +const partial: Partial = { width: 100 }; +``` + +### ✅ Correct Usage +```typescript +// ✅ Correct: Using Object[] for rest parameters +function debounce(func: Function, delay: number): Function { + return (...args: Object[]): void => { + setTimeout(() => func(...args), delay); + }; +} + +// ✅ Correct: Using explicit type +type Handler = () => string; +type Result = string; + +// ✅ Correct: Using optional properties +interface Config { + width: number; + height: number; + color?: string; +} +const partial: Config = { width: 100, height: 0, color: undefined }; +``` + +### Function Type Definitions + +#### Using Explicit Types +```typescript +// ✅ Correct: Explicit function type +private handleClick = (event: ClickEvent): void => { + this.count++; +}; + +// ✅ Correct: Explicit return type +private getValue(): string { + return 'Hello'; +} +``` + +#### Using Interfaces +```typescript +// ✅ Correct: Interface for function type +interface EventHandler { + (event: ClickEvent): void; +} + +private handleClick: EventHandler = (event: ClickEvent): void => { + console.info('Clicked'); +}; +``` + +#### Using Type Aliases +```typescript +// ✅ Correct: Type alias for function type +type ClickHandler = (event: ClickEvent) => void; + +private handleClick: ClickHandler = (event: ClickEvent): void => { + console.info('Clicked'); +}; +``` + +### Rest Parameters + +#### ❌ Wrong +```typescript +// ❌ Wrong: Using Parameters +function wrapper void>(func: T): void { + return (...args: Parameters) => { + func(...args); + }; +} +``` + +#### ✅ Correct +```typescript +// ✅ Correct: Using Object[] +function wrapper(func: Function): Function { + return (...args: Object[]): void => { + func(...args); + }; +} + +// ✅ Correct: Using explicit types +function wrapper(func: (x: number, y: number) => void): (x: number, y: number) => void { + return (x: number, y: number): void => { + func(x, y); + }; +} +``` + +### Debounce Implementation + +#### ❌ Wrong +```typescript +class DebounceUtil { + private timeoutId: number = -1; + + debounce void>(func: T, delay: number): T { + return ((...args: Parameters) => { + clearTimeout(this.timeoutId); + this.timeoutId = setTimeout(() => { + func(...args); + }, delay); + }) as T; + } +} +``` + +#### ✅ Correct +```typescript +class DebounceUtil { + private timeoutId: number = -1; + + debounce(func: Function, delay: number): Function { + return (...args: Object[]): void => { + clearTimeout(this.timeoutId); + this.timeoutId = setTimeout(() => { + func(...args); + }, delay); + }; + } +} + +// ✅ Correct: With explicit types +class DebounceUtil { + private timeoutId: number = -1; + + debounce(func: (event: ClickEvent) => void, delay: number): (event: ClickEvent) => void { + return (event: ClickEvent): void => { + clearTimeout(this.timeoutId); + this.timeoutId = setTimeout(() => { + func(event); + }, delay); + }; + } +} +``` + +### Generic Functions + +#### ❌ Wrong +```typescript +// ❌ Wrong: Using utility types +function wrap R, R>(func: T): T { + return func; +} +``` + +#### ✅ Correct +```typescript +// ✅ Correct: Using explicit types +function wrap(func: (x: number, y: number) => number): (x: number, y: number) => number { + return func; +} + +// ✅ Correct: Using interfaces +interface BinaryFunction { + (x: number, y: number): number; +} + +function wrap(func: BinaryFunction): BinaryFunction { + return func; +} +``` + +### Common Patterns + +#### Event Handlers +```typescript +// ✅ Correct: Event handler type +type ClickHandler = (event: ClickEvent) => void; +type TouchHandler = (event: TouchEvent) => void; +type ScrollHandler = (event: ScrollEvent) => void; + +@Component +struct EventHandlers { + private onClick: ClickHandler = (event: ClickEvent): void => { + console.info('Clicked'); + }; + + private onTouch: TouchHandler = (event: TouchEvent): void => { + console.info('Touched'); + }; + + build() { + Column() { + Button('Click') + .onClick(this.onClick) + } + .onTouch(this.onTouch) + } +} +``` + +#### Callback Functions +```typescript +// ✅ Correct: Callback type +interface SuccessCallback { + (data: string): void; +} + +interface ErrorCallback { + (error: Error): void; +} + +class DataFetcher { + fetchData(success: SuccessCallback, error: ErrorCallback): void { + try { + success('Data loaded'); + } catch (e) { + error(e as Error); + } + } +} +``` + +#### Utility Functions +```typescript +// ✅ Correct: Utility function types +type Mapper = (item: T, index: number) => R; +type Filter = (item: T, index: number) => boolean; +type Reducer = (acc: R, item: T, index: number) => R; + +class ArrayUtils { + static map(array: T[], mapper: Mapper): R[] { + return array.map(mapper); + } + + static filter(array: T[], filter: Filter): T[] { + return array.filter(filter); + } + + static reduce(array: T[], reducer: Reducer, initial: R): R { + return array.reduce(reducer, initial); + } +} +``` + +### Best Practices +1. **Use explicit types**: Always define function types explicitly +2. **Use interfaces**: Define interfaces for complex function types +3. **Use type aliases**: Create type aliases for reusable function types +4. **Avoid utility types**: Don't use TypeScript utility types +5. **Use Object[]**: Use `Object[]` for rest parameters + +### Related Files +- [Code Example](../assets/UtilityTypeError.ets) +- [ArkTS Type System](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-type-system) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/window_rect_size_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/window_rect_size_errors.md new file mode 100644 index 0000000000..17685a855c --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/window_rect_size_errors.md @@ -0,0 +1,70 @@ +# window.Rect 和 window.Size 类型错误 + +## 常见错误 + +### 1. window.Point 不存在 + +``` +Property 'Point' does not exist on type 'Namespace window' +``` + +### 2. window.Size 不存在 + +``` +Property 'size' does not exist on type 'WindowProperties' +``` + +### 3. window.Rect 属性访问错误 + +``` +Property 'x' does not exist on type 'Rect' +Property 'y' does not exist on type 'Rect' +``` + +## 错误原因 + +- `window.Rect` 使用 `left/top` 而非 `x/y` +- `WindowProperties` 没有 `size` 属性,需要从 `windowRect` 获取 +- `window.Point` 类型不存在 + +## 解决方案 + +### window.Rect 正确使用 + +```typescript +// window.Rect 有 left, top, width, height 属性 +const rect: window.Rect = { left: 0, top: 0, width: 100, height: 100 }; +``` + +### 从 WindowProperties 获取尺寸 + +```typescript +const properties = win.getWindowProperties(); +const rect = properties.windowRect; +const width = rect.width; +const height = rect.height; +``` + +### windowSizeChange 事件回调 + +```typescript +win.on('windowSizeChange', (size: window.Size) => { + // size 有 width 和 height 属性 + this.windowRect = { + left: this.windowRect.left, + top: this.windowRect.top, + width: size.width, + height: size.height + }; +}); +``` + +## 详细代码示例 + +请参考 [WindowRectSizeError.ets](../assets/WindowRectSizeError.ets) + +## 相关 API + +- [WindowProperties](https://developer.harmonyos.com/cn/docs/documentation/reference/apis-arkui-window-0000001774129417) +- [window.Rect](https://developer.harmonyos.com/cn/docs/documentation/reference/apis-arkui-window-0000001774129417) +- [window.Size](https://developer.harmonyos.com/cn/docs/documentation/reference/apis-arkui-window-0000001774129417) diff --git a/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/window_type_errors.md b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/window_type_errors.md new file mode 100644 index 0000000000..3af503e1f7 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-error-fixes/reference/window_type_errors.md @@ -0,0 +1,95 @@ +# Window API Type Errors + +## Error: `window.getLastWindow` type inference issue + +### Error Message +``` +Type 'void & Promise' errors +``` + +### Cause +Using async/await pattern with `window.getLastWindow()` causes type inference issues in ArkTS. The function signature doesn't properly resolve to a Promise type when used with await. + +### Solution +Use the callback pattern instead of async/await for `window.getLastWindow()`. + +### Key Points +- Use callback pattern: `window.getLastWindow(context, (err, win) => { ... })` +- Always check error code: `if (err.code !== 0)` +- Handle errors gracefully with console logging +- Use explicit type annotation for size: `(size: window.Size) => void` + +### Callback Pattern +```typescript +window.getLastWindow(context, (err, win) => { + if (err.code !== 0) { + console.error('Failed to get window:', err); + return; + } + + // Use window instance + const properties = win.getWindowProperties(); +}); +``` + +### Window Properties +```typescript +interface WindowProperties { + windowRect: Rect; // Window position and size + type: WindowType; // Window type + mode: WindowMode; // Window mode + brightness: number; // Brightness (0.0-1.0) + isPrivacyMode: boolean; // Privacy mode status + isFullScreen: boolean; // Full screen status + layoutMode: LayoutMode; // Layout mode +} + +interface Rect { + left: number; // Left position + top: number; // Top position + width: number; // Width + height: number; // Height +} +``` + +### Window Size Change Event +```typescript +win.on('windowSizeChange', (size: window.Size) => { + console.info(`New size: ${size.width}x${size.height}`); +}); + +interface Size { + width: number; // New width + height: number; // New height +} +``` + +### Common Window Events +```typescript +win.on('windowSizeChange', (size: window.Size) => { }); +win.on('systemBarTintChange', (region: Region) => { }); +win.on('windowEvent', (data: WindowEvent) => { }); +win.on('avoidAreaChange', (data: AvoidArea) => { }); +``` + +### Error Handling +```typescript +// Check for specific error codes +if (err.code === 1300001) { + // Invalid parameter +} else if (err.code === 1300002) { + // Window not found +} else if (err.code === 1300003) { + // Window operation failed +} +``` + +### Best Practices +1. **Always check error code**: Never assume success +2. **Log errors**: Use console.error for debugging +3. **Clean up listeners**: Remove event listeners in `aboutToDisappear()` +4. **Use type annotations**: Explicit types prevent inference issues +5. **Handle window state**: Account for window resize and orientation changes + +### Related Files +- [Code Example](../assets/WindowTypeError.ets) diff --git a/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/SKILL.md b/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/SKILL.md new file mode 100644 index 0000000000..da1757aa6f --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/SKILL.md @@ -0,0 +1,71 @@ +--- +name: arkts-grammar-standards +description: Load this skill when writing or modifying .ets files. Use it for ArkTS syntax rules, ArkTS-specific restrictions, TypeScript-to-ArkTS syntax differences, syntax compliance review, and ArkTS syntax questions. +--- + +# arkts-grammar-standards + +Use this skill before authoring ArkTS code and to answer ArkTS syntax and restriction questions with grounded references. + +## Core authoring checklist + +Before writing or modifying `.ets` files: + +- Treat the code as ArkTS, not generic TypeScript. +- Do not use `any` or `unknown` unless the user explicitly allows it. +- Do not use `as` type assertions; use explicit types, constructors, or typed helper functions. +- Do not rely on structural typing; prefer named classes, interfaces, and explicit `implements` relationships. +- Do not use dynamic property access such as `obj[key]` as a normal modeling pattern; prefer direct property access with known names. +- Give object literals explicit type context through typed variables, typed parameters, or class/interface construction. +- Do not use inline object literal types; define a named interface or class instead. +- Do not use template literals such as `` `${value}` ``; use string concatenation and explicit conversion. +- Do not use namespaces as runtime values; import or reference the concrete exported value/type that is needed. +- Avoid restricted TypeScript patterns such as destructuring declarations, destructuring parameters, function expressions, nested local function declarations, class expressions, `delete`, `in`, `for...in`, and type queries like `typeof Foo`. + +Prefer the bundled reference files over model memory. Keep the answer focused on: + +- whether a syntax form is allowed +- what ArkTS expects instead +- whether the rule comes from the language guide or from the linter-derived summary +- which topic best matches the user's code or question + +## Reference order + +Read these files as needed: + +1. `references/topic-aliases.json` +2. `references/basic-syntax.md` +3. `references/restrictions.md` +4. `references/ts-diff.md` + +Use `basic-syntax.md` for normal ArkTS writing patterns. +Use `restrictions.md` when the question is about forbidden syntax, restricted operators, object literal rules, `Sendable`, or review comments. +Use `ts-diff.md` when the user is porting TypeScript or asking why a familiar TypeScript pattern does not work in ArkTS. + +## Source rules + +- Treat `basic-syntax.md` and `ts-diff.md` as guide-oriented summaries backed by the bundled ArkTS language guide sections. +- Treat `restrictions.md` as implementation-derived guidance based on the linter summary. Say that clearly when citing it. +- Do not present linter-derived restrictions as if they were verbatim official spec text. +- If both a guide-oriented explanation and a linter restriction apply, mention both and explain the relationship in one or two sentences. + +## Response shape + +Use this format unless the user asks for something else: + +```markdown +- Topic: +- Source: +- Reference: +- Why it matches: +- Guidance: +``` + +If the user shows code, add a short rewrite suggestion after the guidance. + +## Working rules + +- Prefer direct syntax guidance over broad language tutorials. +- Prefer named ArkTS alternatives such as class, interface, explicit field type, arrow function, or direct property access. +- Keep citations short and traceable. +- Do not expand the answer into build, run, debug, or tool workflows unless the user explicitly asks for that after the syntax answer. diff --git a/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/basic-syntax.md b/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/basic-syntax.md new file mode 100644 index 0000000000..3e21c89f75 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/basic-syntax.md @@ -0,0 +1,103 @@ +# ArkTS Basic Syntax + +This file summarizes high-value ArkTS syntax guidance for common authoring and review questions. + +## Variables + +- Use `let` or `const` for variable declarations. +- Prefer `const` when the binding does not change. +- Keep types explicit when inference would be unclear. + +Reference: +- Guide: `docs/ArkTS-Language-Guide/02-Basic-Syntax/*` +- Linter summary: declaration section and `arkts-no-var` + +## Classes + +- Prefer named `class` declarations. +- Declare fields in the class body. +- Use constructors to establish valid state. +- Prefer constructing instances with `new` instead of treating classes as loose object shapes. + +Reference: +- Guide: `docs/ArkTS-Language-Guide/02-Basic-Syntax/classes.md` + +## Interfaces + +- Use named interfaces for reusable contracts. +- Prefer interface or class names over inline object type declarations. +- Use `implements` to make class contracts explicit. + +Reference: +- Guide: `docs/ArkTS-Language-Guide/02-Basic-Syntax/interfaces.md` + +## Functions + +- Prefer arrow functions for function values. +- Keep return types explicit when the result is not obvious. +- Use top-level or class methods for reusable logic instead of nested local function declarations. + +Reference: +- Guide: `docs/ArkTS-Language-Guide/02-Basic-Syntax/functions.md` +- Linter summary: function declaration section + +## Operators + +- Use normal arithmetic, comparison, logical, and conditional operators with explicit types. +- Prefer `===` and `!==`. +- Use explicit conversions instead of JavaScript-style coercion. +- Use string concatenation and explicit conversion instead of template literals. + +Reference: +- Guide: `docs/ArkTS-Language-Guide/02-Basic-Syntax/advanced-operators.md` + +## Strings + +- Use ordinary string literals and concatenation for formatted text. +- Convert non-string values explicitly before concatenating when the target expects a string. +- Avoid template literal syntax such as `` `Count: ${count}` ``. + +ArkTS style: + +```ts +const label: string = "Count: " + count.toString() +``` + +Reference: +- Linter summary: syntax restriction section + +## Object modeling + +- Prefer named classes and interfaces for stable data models. +- Use object literals only when there is clear explicit type context. +- Prefer direct property access with known names. +- Avoid `Record` and dynamic keys when a named interface or class can model the shape. + +ArkTS style: + +```ts +interface UserLabels { + name: string + title: string +} + +const labels: UserLabels = { + name: "Name", + title: "Title", +} +const title: string = labels.title +``` + +Reference: +- Guide: `docs/ArkTS-Language-Guide/02-Basic-Syntax/classes.md` +- Guide: `docs/ArkTS-Language-Guide/02-Basic-Syntax/interfaces.md` +- Linter summary: object literal section + +## Namespaces and imports + +- Treat namespaces as declaration or type organization, not runtime objects to pass around. +- Import or reference the concrete exported class, enum, function, or type that the code needs. +- If a namespace-like module is needed at runtime, model the runtime value with an explicit class or exported object that ArkTS accepts. + +Reference: +- Linter summary: namespace section diff --git a/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/restrictions.md b/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/restrictions.md new file mode 100644 index 0000000000..54065349ba --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/restrictions.md @@ -0,0 +1,100 @@ +# ArkTS Restrictions + +This file summarizes ArkTS-specific restrictions that are especially useful in code review and syntax troubleshooting. + +Unless noted otherwise, the rules below are derived from the bundled linter summary rather than copied from the official guide text. + +## Declarations + +- Do not use `var`; use `let` or `const`. +- Do not use destructuring in variable declarations. +- Do not use destructuring in parameters. +- Do not rely on `any` or `unknown`. +- Do not use `as` type assertions as a shortcut around ArkTS type checking. +- Avoid definite assignment assertions like `!`; in `Sendable` classes they are not allowed. + +Reference: +- Linter summary: declaration section +- Rules: `arkts-no-var`, `arkts-no-destruct-decls`, `arkts-no-destruct-params`, `arkts-no-any-unknown`, `arkts-no-definite-assignment` +- Project authoring rule: avoid `as` type assertions unless explicitly requested. + +## Functions and classes + +- Do not use nested local function declarations. +- Do not use function expressions; prefer arrow functions. +- Do not use generator functions or `yield`. +- Do not use class expressions. +- Do not use standalone `this` in free functions. + +Reference: +- Linter summary: function and class sections +- Rules: `arkts-no-nested-funcs`, `arkts-no-func-expressions`, `arkts-no-generators`, `arkts-no-class-literals`, `arkts-no-standalone-this` + +## Object and property access + +- Do not treat object literals as free-form structural types. +- Do not declare inline object literal types in place of named interfaces or classes. +- Object literals must have explicit type context, such as a typed variable, typed parameter, or declared class/interface target. +- Do not depend on dynamic property access as a normal modeling pattern. +- Avoid `Record` when it encourages dynamic indexing; define a named type with known properties instead. +- Prefer identifier property names and direct dot access. + +Reference: +- Linter summary: object literal and property access sections +- Rules: `arkts-no-structural-typing`, `arkts-no-untyped-obj-literals`, `arkts-no-obj-literals-as-types`, `arkts-no-props-by-index`, `arkts-identifiers-as-prop-names` + +## String syntax + +- Template literals are not supported for ArkTS authoring in this project. +- Rewrite interpolation to string concatenation with explicit conversion. + +Example rewrite: + +```ts +const label: string = "Likes: " + likeCount.toString() +``` + +Reference: +- Linter summary: syntax restriction section + +## Namespaces + +- Do not use a namespace itself as a runtime value. +- Avoid namespace bodies that contain runtime statements; keep namespace-like organization to supported declarations. +- Prefer importing concrete exported symbols or defining explicit runtime classes/objects. + +Reference: +- Linter summary: namespace section +- Rules: `arkts-no-ns-statements` + +## Restricted operators and statements + +- `delete` is not supported. +- `in` and `for...in` are restricted. +- `typeof` is allowed in expression context, but not as a type query. +- `catch` clauses must not carry an explicit exception type annotation. +- Destructuring assignment is not supported. + +Reference: +- Linter summary: operators and statements sections +- Rules: `arkts-no-delete`, `arkts-no-in`, `arkts-no-types-in-catch`, `arkts-no-destruct-assignment`, `arkts-no-type-query` + +## Sendable-focused restrictions + +- `Sendable` classes must use explicit field types. +- `Sendable` field types must themselves be sendable. +- `Sendable` types must not be initialized directly from object literals or array literals. +- `Sendable` classes and functions have stricter capture and inheritance rules. + +Reference: +- Linter summary: sendable sections +- Rules: `arkts-sendable-explicit-field-type`, `arkts-sendable-prop-types`, `arkts-sendable-obj-init`, `arkts-sendable-class-inheritance` + +## How to cite this file + +When using this file in an answer, say that the restriction comes from the linter-derived ArkTS summary. +Use that wording especially for: + +- forbidden syntax claims +- `Sendable` restrictions +- dynamic object model limitations diff --git a/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/topic-aliases.json b/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/topic-aliases.json new file mode 100644 index 0000000000..6b5fe942af --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/topic-aliases.json @@ -0,0 +1,93 @@ +{ + "variables": [ + "var", + "let", + "const", + "变量", + "变量声明" + ], + "destructuring": [ + "destructuring", + "destructure", + "解构", + "解构赋值", + "解构参数" + ], + "functions": [ + "arrow function", + "function expression", + "nested function", + "generator", + "函数表达式", + "箭头函数", + "局部函数" + ], + "classes": [ + "class", + "class expression", + "constructor", + "类", + "构造函数" + ], + "interfaces": [ + "interface", + "inline object type", + "structural typing", + "接口", + "结构类型" + ], + "operators": [ + "delete", + "typeof", + "in operator", + "for in", + "catch", + "as", + "type assertion", + "as const", + "运算符", + "类型查询", + "类型断言" + ], + "object-literal": [ + "object literal", + "dynamic property", + "indexed access", + "record", + "Record", + "property by index", + "对象字面量", + "动态属性", + "动态索引" + ], + "strings": [ + "template literal", + "template string", + "${", + "string interpolation", + "模板字符串", + "字符串插值" + ], + "namespace": [ + "namespace", + "namespace as value", + "Cannot use namespace as a value", + "命名空间", + "namespace 作为值" + ], + "sendable": [ + "sendable", + "@sendable", + "可发送", + "并发类型" + ], + "typescript-diff": [ + "typescript", + "ts", + "migration", + "difference", + "改写", + "迁移", + "差异" + ] +} diff --git a/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/ts-diff.md b/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/ts-diff.md new file mode 100644 index 0000000000..87f3555979 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-grammar-standards/references/ts-diff.md @@ -0,0 +1,108 @@ +# TypeScript to ArkTS Syntax Differences + +This file highlights TypeScript patterns that often need rewriting in ArkTS. + +## Dynamic JavaScript patterns are narrower + +- ArkTS is not a drop-in TypeScript superset in practice. +- Patterns that depend on dynamic object mutation, loose shape matching, or reflective syntax are often rejected. +- Patterns based on `Record`, arbitrary indexes, or namespace objects often need explicit ArkTS models. + +Reference: +- Guide: `docs/ArkTS-Language-Guide/09-Migration-Guide/01-TypeScript-to-ArkTS/typescript-to-arkts-migration-guide.md` +- Linter summary: overview section + +## Prefer named types over inline shapes + +TypeScript style: + +```ts +function printPoint(point: { x: number; y: number }): void {} +``` + +ArkTS style: + +```ts +interface PointLike { + x: number + y: number +} + +function printPoint(point: PointLike): void {} +``` + +Reference: +- Guide: `docs/ArkTS-Language-Guide/02-Basic-Syntax/interfaces.md` + +## Prefer declarations over expressions + +- Rewrite function expressions to arrow functions when the function is used as a value. +- Rewrite class expressions to named class declarations. +- Move nested function declarations to top-level declarations, class methods, or arrow-function values. + +Reference: +- Guide: `docs/ArkTS-Language-Guide/02-Basic-Syntax/classes.md` +- Linter summary: function and class sections + +## Prefer explicit class and interface modeling + +- TypeScript often treats classes as object shapes; ArkTS prefers constructor-based initialization and declared members. +- TypeScript often relies on structural typing; ArkTS narrows that style and expects explicit named contracts. +- TypeScript often uses `Record` or indexed access for flexible maps; ArkTS authoring should prefer named interfaces/classes and direct property access when property names are known. + +Reference: +- Guide: `docs/ArkTS-Language-Guide/02-Basic-Syntax/classes.md` +- Guide: `docs/ArkTS-Language-Guide/02-Basic-Syntax/interfaces.md` +- Linter summary: structural typing restriction + +## Rewrite template strings + +TypeScript style: + +```ts +const label: string = `Count: ${count}` +``` + +ArkTS style: + +```ts +const label: string = "Count: " + count.toString() +``` + +Reference: +- Linter summary: syntax restriction section + +## Rewrite namespace-as-value patterns + +TypeScript style: + +```ts +const api = common +api.doWork() +``` + +ArkTS style: + +```ts +common.doWork() +``` + +If the code needs a runtime object, define that object explicitly instead of using a namespace as the value. + +Reference: +- Linter summary: namespace section + +## Remove unsupported or narrowed syntax + +Common rewrites: + +- `var` -> `let` or `const` +- destructuring declaration -> explicit local bindings +- `value as T` -> typed variable, typed function return, constructor, or explicit conversion +- `` `${value}` `` -> string concatenation with explicit conversion +- `delete obj.x` -> construct the desired value without runtime property deletion +- `catch (err: Error)` -> `catch (err)` +- `type T = typeof Foo` -> use the explicit type you need instead of a type query + +Reference: +- Linter summary: declaration, operator, and statement sections diff --git a/src/crates/assembly/core/builtin_skills/arkts-runtime-fix/SKILL.md b/src/crates/assembly/core/builtin_skills/arkts-runtime-fix/SKILL.md new file mode 100644 index 0000000000..d09ef109de --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/arkts-runtime-fix/SKILL.md @@ -0,0 +1,152 @@ +--- +name: arkts-runtime-fix +description: Load for ArkTS/JavaScript jscrash, runtime crash, uncaught exception, stack trace, faultlog, or hilog diagnosis. Also load when the app 闪退/崩溃/白屏, exits after 点击/启动/launch, or build succeeds but runtime fails (no compile error). Use before broad Read/Glob on crash-only tasks. +--- + +# Harmony JSCrash Fixes + +Use this skill to diagnose and fix ArkTS or JavaScript runtime crashes with minimal edits. + +Use this skill's private Node scripts under `skills/arkts-runtime-fix/scripts/` to parse crash evidence, inspect recent faultlogger entries, or collect hilog when no better evidence is available. + +If `node` is unavailable, stop and explain that the private scripts cannot run. + +## When To Load + +Load this skill when the issue looks like one of these: + +- Runtime logs show `TypeError`, `ReferenceError`, `RangeError`, `SyntaxError`, `BusinessError`, or similar exceptions. +- The app exits, flashes back, or white-screens during launch or after a tap. +- The user provides a `jscrash` log, stack trace, or a temporary log file with `@file`. +- Build succeeds, but runtime behavior fails immediately. + +## Core Approach + +Prefer a concrete crash anchor before broad code exploration. A good anchor can come from: + +- a provided crash log +- a stack trace +- a clear page or module named by the user +- a recent device-side faultlog or hilog when no better evidence is available + +Avoid broad `Read` / `Glob` / `Explore` across the whole project until you have at least one concrete anchor such as: + +- `error_type` +- `error_message` +- `suspected_file` +- `top_stack` +- or a clearly named crash entry point from the user + +Do not over-collect logs. If the user already gave enough crash evidence, parse that evidence first and move into focused reading and minimal fixes. + +## Tool And Script Contract + +### Private Node scripts + +Run the private scripts through Shell like this: + +```bash +node "{SKILL_DIR}/scripts/