We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/PoivronMax/idlize-cj-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
# ArkTS → Cangjie 语法翻译说明
本文档基于 `pay-subset-no-gesture` 测试集的转换实践,总结 ArkTS 到 Cangjie 的语法翻译规则。
## 关于 ArkTS
ArkTS 是 OpenHarmonyOS 的应用开发语言,基于 TypeScript 扩展。本文档使用的测试集为类型定义文件(`.d.ts`),不包含具体实现代码。
### 语法基础
ArkTS 基于 TypeScript,完全兼容 TypeScript 类型系统,支持联合类型、可选类型、泛型、类与接口、枚举等特性,同时支持 `readonly` 修饰符和静态成员。
### 项目增强
本项目通过自定义 Parser 扩展了精确数值类型支持,标准 TypeScript 仅提供 `number` 类型:
- `int`/`long`/`float`/`double`(标准 TypeScript 仅支持 `number`)
- 语义化类型推断功能已移除,`number` 统一映射为 `Float64`
## 目录
1. [通用类型翻译规则](#第一部分通用类型翻译规则)(当前文件)
2. [组件类型翻译规则](02-第二部分:组件类型翻译规则.md)
3. [参数类型翻译规则](03-第三部分:参数类型翻译规则.md)
4. [特殊语法翻译](04-第四部分:特殊语法翻译.md)
5. [转换差异总结](05-第五部分:转换差异总结.md)
6. [代码生成示例](06-第六部分:代码生成示例.md)
---
## 第一部分:通用类型翻译规则
### 1.1 基本类型映射
基本类型映射是转换的基础,包括字符串、布尔、数值、空值这些类型。下面看看各类型在测试集中的使用情况和转换规则。
#### 1.1.1 字符串和布尔类型
##### 1.1.1.1 string → String
`string` 类型直接映射为 `String`,这个比较简单。看几个使用场景:
在接口属性中的使用:
```typescript
// button.d.ts
declare interface ButtonConfiguration {
label: string; // 按钮文本标签
}
```
在方法参数中的使用:
```typescript
// text.d.ts
fontFeature(value: string): TextAttribute;
```
在类型别名中的使用:
```typescript
// units.d.ts
type PX = string; // 像素单位字符串
type FP = string; // 字体像素单位字符串
type LPX = string; // 逻辑像素单位字符串
type Percentage = string; // 百分比字符串
type Degree = string; // 角度单位字符串
```
转换后的 Cangjie 代码:
```cangjie
// 接口属性
public class ButtonConfiguration {
public var label: String
// ...
}
// 类型别名
public typealias PX = String
public typealias FP = String
public typealias LPX = String
public typealias Percentage = String
public typealias Degree = String
```
转换规则:`string` → `String`
##### 1.1.1.2 boolean → Bool
`boolean` 类型映射为 `Bool`,也很直接。看几个例子:
在接口属性中的使用:
```typescript
// button.d.ts
declare interface ButtonConfiguration {
pressed: boolean; // 按钮是否被按下
}
```
可选布尔属性的使用:
```typescript
// common.d.ts
declare interface InputCounterOptions {
highlightBorder?: boolean; // 是否高亮边框,默认 true
}
```
转换后的 Cangjie 代码:
```cangjie
// 接口属性
public class ButtonConfiguration {
public var pressed: Bool
// ...
}
public class InputCounterOptions {
public var highlightBorder: Bool = true
// ...
}
```
转换规则:`boolean` → `Bool`
##### 1.1.1.3 void → Unit
`void` 表示无返回值,主要用于函数返回类型。看几个使用场景:
在回调函数返回类型中的使用:
```typescript
// button.d.ts
type ButtonTriggerClickCallback = (xPos: double, yPos: double) => void;
// units.d.ts
type VoidCallback = () => void;
```
在方法返回类型中的使用:
```typescript
// text.d.ts
closeSelectionMenu(): void;
setStyledString(value: StyledString): void;
```
转换后的 Cangjie 代码:
```cangjie
// 回调类型
public typealias ButtonTriggerClickCallback = (xPos: Float64, yPos: Float64) => Unit
public typealias VoidCallback = () => Unit
// 方法返回类型
public func closeSelectionMenu(): Unit {
// ...
}
public func setStyledString(value: StyledString): Unit {
// ...
}
```
转换规则:`void` → `Unit`,`() => void` → `() => Unit`
##### 1.1.1.4 undefined 类型处理
`undefined` 表示未定义,常用于表示可选值。看几个例子:
在接口属性中的使用:
```typescript
// common.d.ts
declare interface TextDecorationOptions {
type: TextDecorationType | undefined; // 可以是 undefined
}
```
在方法参数中的使用:
```typescript
// button.d.ts
type(value: ButtonType | undefined): ButtonAttribute;
```
转换后的 Cangjie 代码:
```cangjie
// Component 层示例
public func type_(value: Option<ButtonType>): This {
this.getPeer().typeAttribute(value)
return this
}
```
转换规则:`Type | undefined` → `Option<Type>`,`undefined` 值对应 `Option.None`
#### 1.1.2 精确数值类型(项目增强)
`int`/`long`/`float`/`double` 是本项目对 ArkTS 的增强,由自定义 Parser 处理。标准 TypeScript 只提供 `number` 类型,我们扩展了精确数值类型。
类型定义:
- `int` / `i32`:32 位有符号整数
- `long` / `i64`:64 位有符号整数
- `float` / `f32`:32 位单精度浮点数
- `double` / `f64`:64 位双精度浮点数
看几个使用场景:
在回调函数参数中的使用:
```typescript
// button.d.ts
type ButtonTriggerClickCallback = (xPos: double, yPos: double) => void;
```
在接口属性定义中的使用:
```typescript
// common.d.ts
declare interface InputCounterOptions {
thresholdPercentage?: double;
}
```
在资源 ID 定义中的使用:
```typescript
// units.d.ts
export interface Resource {
id: long; // 资源 ID,使用 64 位整数
}
```
转换后的 Cangjie 代码:
```cangjie
// 回调类型
public typealias ButtonTriggerClickCallback = (xPos: Float64, yPos: Float64) => Unit
// 接口属性
public class InputCounterOptions {
public var thresholdPercentage: Float64 = 0.0
// ...
}
public class Resource {
public var id: Int64 // 资源 ID
// ...
}
```
转换规则:
- `int` / `i32` → `Int32`
- `long` / `i64` → `Int64`
- `float` / `f32` → `Float32`
- `double` / `f64` → `Float64`
#### 1.1.3 number 类型处理
标准 TypeScript/ArkTS 的 `number` 类型统一映射为 `Float64`。我们移除了语义推断模块,不再根据属性名进行类型推断,这样更简单直接。
`number` 是标准 TypeScript/ArkTS 的数值类型(IEEE 754 双精度浮点数)。测试集中大部分数值属性已经用了精确类型,只有少数场景还在用 `number`。
转换后的 Cangjie 代码:
```cangjie
// 统一映射为 Float64
public var someProperty: Float64 = 1.0
```
转换规则:`number` → `Float64`(统一映射,无语义推断)
#### 1.1.4 基本类型映射总述表
下面这个表总结了所有基本类型从 ArkTS 到 Cangjie 的映射规则:
| ArkTS 类型 | Cangjie 类型 | 说明 | 示例用法 | 转换示例 |
|-----------|-------------|------|---------|---------|
| `string` | `String` | 字符串类型,直接映射 | `label: string` | `label: String` |
| `boolean` | `Bool` | 布尔类型,直接映射 | `pressed: boolean` | `pressed: Bool` |
| `void` | `Unit` | 无返回值类型,用于函数返回 | `() => void` | `() => Unit` |
| `undefined` | `Option<T>` | 未定义值,转换为 Option | `value: Type \| undefined` | `value: Option<Type>` |
| `int` / `i32` | `Int32` | 32位整数(项目增强) | `maxLines: int` | `maxLines: Int32` |
| `long` / `i64` | `Int64` | 64位整数(项目增强) | `id: long` | `id: Int64` |
| `float` / `f32` | `Float32` | 32位浮点数(项目增强) | `tempo: float` | `tempo: Float32` |
| `double` / `f64` | `Float64` | 64位浮点数(项目增强) | `xPos: double` | `xPos: Float64` |
| `number` | `Float64` | 标准 TypeScript 数值类型,统一映射 | `someProperty: number` | `someProperty: Float64` |
### 1.2 可选类型(Optional)
#### 1.2.1 `?` 标识符概述
在 ArkTS 中,`?` 标识符有三种用法:
1. 可选属性:`property?: Type` - 用于接口、类、类型别名中的可选属性
2. 可选函数参数:`param?: Type` - 用于函数/方法参数,调用时可以不传
3. 可选链操作符:`?.` - 用于安全访问可能为 null/undefined 的属性或方法
注意:这里只处理 `?` 标识符的用法。`Type | undefined` 这种联合类型语法在 1.7 节"联合类型"中处理。
`param?: Type` 与 `param: T | undefined` 的区别:
| 特性 | `param?: Type` | `param: T \| undefined` |
|------|----------------|---------------------|
| 调用时是否必须传参 | ❌ 可以不传 | ✅ 必须传(可传 undefined) |
| 参数是否存在 | 可能不存在 | 一定存在 |
| 语义 | "参数可选" | "值可为 undefined" |
| 处理章节 | 本节(1.2) | 联合类型章节(1.6) |
#### 1.2.2 可选属性(`property?: Type`)
可选属性用 `?:` 语法,表示该属性可以不存在。常见于接口、类、类型别名的属性定义中。测试集中共出现 787 次,用得挺多的。
基础类型可选属性的示例:
```typescript
// common.d.ts
declare interface InputCounterOptions {
thresholdPercentage?: double; // 可选浮点数
highlightBorder?: boolean; // 可选布尔值
}
```
复杂类型可选属性的示例:
```typescript
// button.d.ts
declare interface ButtonOptions {
type?: ButtonType; // 可选枚举
stateEffect?: boolean; // 可选布尔值
buttonStyle?: ButtonStyleMode; // 可选枚举
controlSize?: ControlSize; // 可选枚举
role?: ButtonRole; // 可选枚举
}
```
接口类型可选属性的示例:
```typescript
// button.d.ts
declare interface ButtonLabelStyle {
overflow?: TextOverflow; // 可选枚举
font?: Font; // 可选接口类型
maxLines?: int; // 可选整数
}
```
所有可选属性统一转换为 `Option<T>`,转换后的代码:
```cangjie
// 基础类型可选属性
public class InputCounterOptions {
public var thresholdPercentage: Option<Float64>
public var highlightBorder: Option<Bool>
public init(
thresholdPercentage: Option<Float64>,
highlightBorder: Option<Bool>
) {
this.thresholdPercentage = thresholdPercentage
this.highlightBorder = highlightBorder
}
}
// 复杂类型可选属性
public class ButtonOptions {
public var type_: Option<ButtonType>
public var stateEffect: Option<Bool>
public var buttonStyle: Option<ButtonStyleMode>
public var controlSize: Option<ControlSize>
public var role: Option<ButtonRole>
public init(
type_: Option<ButtonType>,
stateEffect: Option<Bool>,
buttonStyle: Option<ButtonStyleMode>,
controlSize: Option<ControlSize>,
role: Option<ButtonRole>
) {
this.type_ = type_
this.stateEffect = stateEffect
this.buttonStyle = buttonStyle
this.controlSize = controlSize
this.role = role
}
}
```
转换规则:
| ArkTS 类型 | Cangjie 类型 |
|-----------|-------------|
| `property?: boolean` | `property: Option<Bool>` |
| `property?: int` | `property: Option<Int32>` |
| `property?: long` | `property: Option<Int64>` |
| `property?: float` | `property: Option<Float32>` |
| `property?: double` | `property: Option<Float64>` |
| `property?: string` | `property: Option<String>` |
| `property?: EnumType` | `property: Option<EnumType>` |
| `property?: InterfaceType` | `property: Option<InterfaceType>` |
| `property?: ClassType` | `property: Option<ClassType>` |
所有可选属性统一用 `Option<T>` 包装,不用默认值策略。序列化时用 `RuntimeType.UNDEFINED` 标记 `Option.None` 值。
#### 1.2.3 可选函数参数(`param?: Type`)
可选函数参数用 `?` 标识符,表示调用时可以不传该参数。
测试集中没发现 `param?: Type` 这种可选参数语法。方法参数都用 `param: Type | undefined` 联合类型语法:
```typescript
// button.d.ts - 使用联合类型,非可选参数语法
declare class ButtonAttribute {
type(value: ButtonType | undefined): ButtonAttribute;
stateEffect(value: boolean | undefined): ButtonAttribute;
fontSize(value: Length | undefined): TextAttribute;
}
// text.d.ts - 构造器参数也使用联合类型
declare interface TextInterface {
(content?: string | Resource, value?: TextOptions): TextAttribute;
}
```
测试集中不存在 `param?: Type` 语法,当前项目没生成相关翻译代码。
预期转换规则:
如果未来遇到 `param?: Type` 语法,预期用重载处理:
```typescript
// ArkTS 可选参数
function greet(name?: string): void {
console.log(name ?? "Guest");
}
```
转换为 Cangjie 的两个重载版本:
```cangjie
// 重载1:不传参数
public func greet(): Unit {
// 调用重载2,传入 Option.None
this.greet(Option.None)
}
// 重载2:传入 Option 参数
public func greet(name: Option<String>): Unit {
// 实际实现
let actualName = match (name) {
case Some(n) => n
case None => "Guest"
}
println(actualName)
}
```
可选参数通过生成两个重载方法实现,无参版本调用有参版本并传入 `Option.None`。测试集中没用可选参数语法,所有方法参数都用 `Type | undefined` 联合类型(见 1.7 节)。
#### 1.2.4 可选链操作符(`?.`)
可选链操作符 `?.` 用来安全访问可能为 null/undefined 的属性或方法。
测试集中没发现可选链操作符的使用。测试集主要是类型定义文件(`.d.ts`),不包含实现代码。
预期转换规则:
可选链操作符属于运行时表达式,不属于类型系统的一部分。如果需要处理,ArkTS 的可选链操作符需要在 Cangjie 中用 `match` 表达式或 `if let` 模式匹配实现。
可选链操作符是运行时特性,不在类型翻译范围内。
#### 1.2.5 可选类型处理总结
测试集统计:
- 可选属性(`property?: Type`):787 次使用,已完整支持
- 可选函数参数(`param?: Type`):0 次使用,未涉及
- 可选链操作符(`?.`):0 次使用,不在类型翻译范围内
Option 类型用法:
- `Option.Some(value)` - 表示有值
- `Option.None` - 表示无值
- `if (let Some(x) <- option)` - 模式匹配解包
需要注意的:
1. 所有可选属性统一转换为 `Option<T>`,不用默认值策略
2. `property?: Type` 和 `property: Type | undefined` 在 Cangjie 中翻译结果相同,都转换为 `Option<Type>`
3. 序列化细节见《ArkTS-Cangjie 序列化规则》文档
### 1.3 数组类型
#### 1.3.1 数组类型语法概述
在 ArkTS 中,数组类型有两种等价的语法:
1. 泛型语法:`Array<T>`
2. 数组字面量语法:`T[]`
两种语法在语义上完全等价,用哪个都行。
#### 1.3.2 `Array<T>` 泛型语法
`Array<T>` 是泛型数组语法,其中 `T` 可以是任意类型。看几个使用场景:
在函数参数和返回值中的使用:
```typescript
// text_common.d.ts
type OnCreateMenuCallback = (menuItems: Array<TextMenuItem>) => Array<TextMenuItem>;
```
在联合类型数组中的使用:
```typescript
// units.d.ts
declare interface LocalizedResource {
params?: Array<string | int | long | double | Resource>;
}
// text.d.ts - 联合类型数组作为方法参数
textShadow(value: ShadowOptions | Array<ShadowOptions> | undefined): TextAttribute;
```
`Array<T>` 统一转换为 `ArrayList<T>`,转换后的代码:
```cangjie
// 函数类型
public typealias OnCreateMenuCallback = (menuItems: ArrayList<TextMenuItem>) -> ArrayList<TextMenuItem>
// 可选数组属性
public class LocalizedResource {
// Array<string | int | long | double | Resource>
// 联合类型简化为 Any
public var params: Option<ArrayList<Any>>
// ...
}
```
转换规则:`Array<T>` → `ArrayList<T>`,`Array<T> | undefined` → `Option<ArrayList<T>>`
#### 1.3.3 `T[]` 数组字面量语法
`T[]` 是数组字面量语法,是 `Array<T>` 的简写形式。看几个例子:
在基础类型数组中的使用:
```typescript
// grid.d.ts - 基础类型数组
declare interface GridLayoutOptions {
irregularIndexes?: int[]; // 可选整数数组
}
// image_common.d.ts - 枚举类型数组
declare interface ImageAnalyzerConfig {
types: ImageAnalyzerType[]; // 枚举数组
}
declare interface ImageAIOptions {
types?: ImageAnalyzerType[]; // 可选枚举数组
}
// image.d.ts - number 类型数组
declare interface DrawableDescriptor {
fXDivs?: number[];
fYDivs?: number[];
fRectTypes?: number[];
fColors?: number[];
}
// units.d.ts - 构造器参数
declare class Matrix4Transit {
constructor(value: double[]);
}
// text_common.d.ts - 联合类型中的数组
declare interface TextDataDetectorConfig {
types: TextDataDetectorType[] | undefined | null;
}
```
`T[]` 统一转换为 `ArrayList<T>`,转换后的代码:
```cangjie
// 基础类型数组
public class GridLayoutOptions {
public var irregularIndexes: Option<ArrayList<Int32>>
// ...
}
// 枚举类型数组
public class ImageAnalyzerConfig {
public var types: ArrayList<ImageAnalyzerType>
// ...
}
public class ImageAIOptions {
public var types: Option<ArrayList<ImageAnalyzerType>>
// ...
}
// number 类型数组(number → Float64)
public class DrawableDescriptor {
public var fXDivs: Option<ArrayList<Float64>>
public var fYDivs: Option<ArrayList<Float64>>
public var fRectTypes: Option<ArrayList<Float64>>
public var fColors: Option<ArrayList<Float64>>
// ...
}
// 构造器参数
public class Matrix4Transit {
public init(value: ArrayList<Float64>) {
// ...
}
}
```
转换规则:`T[]` → `ArrayList<T>`,`T[] | undefined` → `Option<ArrayList<T>>`
#### 1.3.4 数组类型翻译规则总述
无论用 `Array<T>` 还是 `T[]` 语法,都统一转换为 `ArrayList<T>`。
| ArkTS 语法 | Cangjie 类型 | 说明 | 示例 |
|-----------|-------------|------|------|
| `Array<string>` | `ArrayList<String>` | 字符串数组 | `params: Array<string>` → `params: ArrayList<String>` |
| `string[]` | `ArrayList<String>` | 字符串数组(简写) | `names: string[]` → `names: ArrayList<String>` |
| `Array<int>` | `ArrayList<Int32>` | 整数数组 | `ids: Array<int>` → `ids: ArrayList<Int32>` |
| `int[]` | `ArrayList<Int32>` | 整数数组(简写) | `irregularIndexes: int[]` → `irregularIndexes: ArrayList<Int32>` |
| `double[]` | `ArrayList<Float64>` | 浮点数组 | `matrix: double[]` → `matrix: ArrayList<Float64>` |
| `number[]` | `ArrayList<Float64>` | number 数组 | `values: number[]` → `values: ArrayList<Float64>` |
| `EnumType[]` | `ArrayList<EnumType>` | 枚举数组 | `types: ImageAnalyzerType[]` → `types: ArrayList<ImageAnalyzerType>` |
| `InterfaceType[]` | `ArrayList<InterfaceType>` | 接口数组 | `items: TextMenuItem[]` → `items: ArrayList<TextMenuItem>` |
| `property?: T[]` | `property: Option<ArrayList<T>>` | 可选数组属性 | `types?: ImageAnalyzerType[]` → `types: Option<ArrayList<ImageAnalyzerType>>` |
| `T[] \| undefined` | `Option<ArrayList<T>>` | 联合类型数组 | `types: T[] \| undefined` → `types: Option<ArrayList<T>>` |
需要注意的:
1. 所有数组类型统一转换为 `ArrayList<T>`,不用 Cangjie 的原生数组类型
2. 可选数组属性转换为 `Option<ArrayList<T>>`,而不是 `ArrayList<Option<T>>`
3. 数组元素类型遵循基础类型映射规则
4. 联合类型数组通常简化为 `ArrayList<Any>`
5. 序列化细节见《ArkTS-Cangjie 序列化规则》文档
测试集统计:`Array<T>` 约 10 次,`T[]` 约 20 次,统一转换为 `ArrayList<T>`
#### 1.3.5 联合类型数组(遗留问题)
当数组元素本身是联合类型时,有两种情况:
1. 数组本身与其他类型的联合:`ShadowOptions | Array<ShadowOptions>`
- 这种情况通过重载处理,生成两个方法版本
2. 数组元素是联合类型:`Array<string | int | long | double | Resource>`
- 这种情况目前存在处理不一致的问题
测试集中的用法:
场景1:数组元素是复杂联合类型
```typescript
// units.d.ts - Resource 接口
declare interface Resource {
bundleName: string;
moduleName: string;
id: long;
// 数组元素是 5 种类型的联合
params?: Array<string | int | long | double | Resource>;
type?: int;
}
```
场景2:数组本身与单个类型的联合(通过重载处理)
```typescript
// text.d.ts - 数组与单个对象的联合
textShadow(value: ShadowOptions | Array<ShadowOptions> | undefined): TextAttribute;
```
转换为 Cangjie 的两个重载:
```cangjie
// 重载1:单个对象
public func textShadow(value: Option<ShadowOptions>): This {
this.getPeer().textShadowAttribute(value)
return this
}
// 重载2:数组
public func textShadow(value: Option<ArrayList<ShadowOptions>>): This {
this.getPeer().textShadowAttribute(value)
return this
}
```
场景3:可选数组参数(通过默认值处理)
```typescript
// common.d.ts - 可选数组参数
expandSafeArea(
types?: Array<SafeAreaType> | undefined,
edges?: Array<SafeAreaEdge> | undefined
): T;
```
转换为 Cangjie:
```cangjie
public func expandSafeArea(
types!: Option<ArrayList<SafeAreaType>> = Option.None,
edges!: Option<ArrayList<SafeAreaEdge>> = Option.None
): This {
this.getPeer().expandSafeAreaAttribute(types, edges)
return this
}
```
场景4:数组类型别名的联合(复杂场景)
```typescript
// common.d.ts - 多个数组类型的联合
declare type BindableResourceStrArray =
ResourceStr[] |
Bindable<ResourceStr[]> |
Bindable<Resource[]> |
Bindable<string[]>;
```
当前处理方式:
对于 `Array<string | int | long | double | Resource>` 这种复杂联合类型数组,预期应该保留联合类型,生成 `ArrayList<Union_String_Int32_Int64_Float64_Resource>`。但实际情况是 `Resource` 接口在生成的 Cangjie 代码中没找到对应的类定义,可能被过滤或简化处理了。
问题分析:
1. 类型复杂度:数组元素包含 5 种类型的联合
2. 递归引用:`Resource` 接口中的 `params` 属性又包含 `Resource` 类型,形成递归
3. 处理策略不明确:是否应该为联合类型生成 Selector 类?是否应该简化为 `ArrayList<Any>`?
建议处理方式:
| 场景 | ArkTS 语法 | 建议 Cangjie 转换 | 说明 |
|------|-----------|------------------|------|
| 数组与单类型联合 | `T \| Array<T>` | 重载:`func(T)` + `func(ArrayList<T>)` | ✅ 已实现 |
| 可选数组参数 | `param?: Array<T>` | `param!: Option<ArrayList<T>> = Option.None` | ✅ 已实现 |
| 简单联合类型数组 | `Array<T1 \| T2>` | `ArrayList<Union_T1_T2>` | ⚠️ 需要生成 Union Selector |
| 复杂联合类型数组 | `Array<string \| int \| Resource>` | `ArrayList<Any>` | ⚠️ 简化处理(遗留问题) |
| 递归类型数组 | `Array<T>` (T 包含自身) | `ArrayList<Any>` | ⚠️ 避免无限递归 |
遗留问题总结:
1. `Array<string | int | long | double | Resource>` 这种复杂联合类型数组的处理策略不明确(当前状态:未生成对应代码或简化为 `Any`,影响范围:仅 1 处使用)
2. 递归类型引用的处理:`Resource` 接口的 `params` 属性包含 `Resource` 类型,需要避免无限递归生成
3. 数组类型别名联合的处理:`BindableResourceStrArray` 包含多个数组类型的联合,需要明确是否展开为重载或简化处理
后续工作:需要在联合类型章节中详细说明联合类型的处理策略,明确复杂联合类型数组的统一处理规则。
### 1.4 元组类型(Tuple Types)
#### 1.4.1 元组类型概述
元组类型(Tuple)是一种固定长度和固定类型的数组,每个位置的元素类型可以不同。在 ArkTS 中,元组用方括号 `[]` 表示,元素类型用逗号分隔。
元组的特点:
- 固定长度:元组的长度在定义时确定
- 类型固定:每个位置的元素类型固定
- 类型可异构:不同位置可以是不同类型
常见用法:属性类型、函数返回值、元组数组
#### 1.4.2 简单元组类型
简单元组类型指元素类型都是基础类型或简单类型的元组。看个例子:
```typescript
// grid.d.ts - 二元组作为属性类型
declare interface GridLayoutOptions {
// 网格大小:[行数, 列数]
regularSize: [int, int];
// 回调函数返回二元组
onGetIrregularSizeByIndex?: (index: int) => [int, int];
// 回调函数返回四元组
onGetRectByIndex?: (index: int) => [int, int, int, int];
}
```
转换结果:
简单元组类型转换为 Cangjie 的元组语法 `(Type1, Type2, ...)`:
```cangjie
public class GridLayoutOptions {
// 二元组属性
public var regularSize: (Int32, Int32)
// 回调函数返回二元组
public var onGetIrregularSizeByIndex: Option<((index: Int32) -> (Int32, Int32))>
// 回调函数返回四元组
public var onGetRectByIndex: Option<((index: Int32) -> (Int32, Int32, Int32, Int32))>
public init(
regularSize: (Int32, Int32),
irregularIndexes: Option<ArrayList<Int32>>,
onGetIrregularSizeByIndex: Option<((index: Int32) -> (Int32, Int32))>,
onGetRectByIndex: Option<((index: Int32) -> (Int32, Int32, Int32, Int32))>
) {
this.regularSize = regularSize
this.irregularIndexes = irregularIndexes
this.onGetIrregularSizeByIndex = onGetIrregularSizeByIndex
this.onGetRectByIndex = onGetRectByIndex
}
}
```
转换规则:`[int, int]` → `(Int32, Int32)`,`[int, int, int, int]` → `(Int32, Int32, Int32, Int32)`
序列化处理:
元组序列化时按索引顺序依次序列化每个元素:
```cangjie
// 序列化二元组
let valueHolderForRegularSize = value.regularSize
let valueHolderForRegularSize_0 = valueHolderForRegularSize[0]
valueSerializer.writeNumber(Option.Some(valueHolderForRegularSize_0))
let valueHolderForRegularSize_1 = valueHolderForRegularSize[1]
valueSerializer.writeNumber(Option.Some(valueHolderForRegularSize_1))
```
#### 1.4.3 复杂元组类型
复杂元组类型指元素包含联合类型、接口类型等复杂类型的元组。看个例子:
```typescript
// common.d.ts - 元组包含联合类型
declare interface SweepGradientOptions {
// 中心点:[长度, 长度],Length 是联合类型
center: [Length, Length];
// 渐变色数组:每个元素是 [颜色, 位置] 元组
colors: Array<[ResourceColor, double]>;
start?: double | string;
end?: double | string;
rotation?: double | string;
repeating?: boolean;
}
// Length 是联合类型
type Length = string | double | Resource;
```
转换结果:
复杂元组类型转换为 Cangjie 的元组语法,元素类型遵循相应的映射规则:
```cangjie
public class SweepGradientOptions {
// Length 联合类型的二元组
public var center: (Length, Length)
public var start: Option<Float64>
public var end: Option<Float64>
public var rotation: Option<Float64>
// 元组数组:ArrayList<(ResourceColor, Float64)>
public var colors: ArrayList<(ResourceColor, Float64)>
public var repeating: Option<Bool>
public init(
center: (Length, Length),
start: Option<Float64>,
end: Option<Float64>,
rotation: Option<Float64>,
colors: ArrayList<(ResourceColor, Float64)>,
repeating: Option<Bool>
) {
this.center = center
this.start = start
this.end = end
this.rotation = rotation
this.colors = colors
this.repeating = repeating
}
}
```
转换规则:`[Length, Length]` → `(Length, Length)`,`Array<[ResourceColor, double]>` → `ArrayList<(ResourceColor, Float64)>`
序列化处理:
复杂元组的序列化需要处理每个元素的类型:
```cangjie
// 序列化 Length 联合类型的元组元素
let valueHolderForCenter = value.center
let valueHolderForCenter_0 = valueHolderForCenter[0]
// Length 是联合类型,需要根据 selector 选择序列化方式
if (valueHolderForCenter_0.getSelector() == 0) {
valueSerializer.writeInt8(Int8(0))
let valueHolderForCenter_0ForIdx0 = valueHolderForCenter_0.getValue0()
valueSerializer.writeString(valueHolderForCenter_0ForIdx0) // string 分支
} else if (valueHolderForCenter_0.getSelector() == 1) {
valueSerializer.writeInt8(Int8(1))
let valueHolderForCenter_0ForIdx1 = valueHolderForCenter_0.getValue1()
valueSerializer.writeNumber(valueHolderForCenter_0ForIdx1) // double 分支
} else if (valueHolderForCenter_0.getSelector() == 2) {
valueSerializer.writeInt8(Int8(2))
let valueHolderForCenter_0ForIdx2 = valueHolderForCenter_0.getValue2()
Resource_serializer.write(valueSerializer, valueHolderForCenter_0ForIdx2) // Resource 分支
}
```
#### 1.4.4 元组类型翻译规则总述
ArkTS 的元组类型 `[Type1, Type2, ...]` 统一转换为 Cangjie 的元组语法 `(Type1, Type2, ...)`。
| ArkTS 语法 | Cangjie 类型 | 说明 | 示例 |
|-----------|-------------|------|------|
| `[int, int]` | `(Int32, Int32)` | 二元组 | `regularSize: [int, int]` → `regularSize: (Int32, Int32)` |
| `[int, int, int, int]` | `(Int32, Int32, Int32, Int32)` | 四元组 | `rect: [int, int, int, int]` → `rect: (Int32, Int32, Int32, Int32)` |
| `[Length, Length]` | `(Length, Length)` | 联合类型元组 | `center: [Length, Length]` → `center: (Length, Length)` |
| `[ResourceColor, double]` | `(ResourceColor, Float64)` | 混合类型元组 | `color: [ResourceColor, double]` → `color: (ResourceColor, Float64)` |
| `Array<[T1, T2]>` | `ArrayList<(T1, T2)>` | 元组数组 | `colors: Array<[ResourceColor, double]>` → `colors: ArrayList<(ResourceColor, Float64)>` |
| `() => [T1, T2]` | `() -> (T1, T2)` | 返回元组的函数 | `getSize: () => [int, int]` → `getSize: () -> (Int32, Int32)` |
| `(param) => [T1, T2]` | `(param) -> (T1, T2)` | 回调返回元组 | `(index: int) => [int, int]` → `(index: Int32) -> (Int32, Int32)` |
需要注意的:
1. 语法差异:ArkTS 用方括号 `[]`,Cangjie 用圆括号 `()`
2. 元素类型映射:元组中的每个元素类型遵循相应的类型映射规则
3. 元组访问:Cangjie 中用索引访问元组元素(`tuple[0]`、`tuple[1]`)
4. 元组数组:`Array<[T1, T2]>` 转换为 `ArrayList<(T1, T2)>`
5. 可选元组:`property?: [T1, T2]` 转换为 `property: Option<(T1, T2)>`(整个元组作为 `Option` 的类型参数)
6. 序列化顺序:元组按索引顺序依次序列化,详细规则见《ArkTS-Cangjie 序列化规则》文档
测试集统计:二元组约 5 次,四元组约 1 次,元组数组约 2 次,统一转换为 Cangjie 的圆括号元组语法
### 1.5 泛型(Generics)
#### 1.5.1 泛型概述
ArkTS 类型说明:
泛型是一种参数化类型的机制,允许在定义类、接口、函数时使用类型参数,在使用时再指定具体类型。泛型提供了代码复用和类型安全的能力。
泛型的常见用法:
1. 泛型接口:`interface Foo<T> { value: T }`
2. 泛型类:`class Bar<T> { ... }`
3. 泛型类型别名:`type Nullable<T> = T | undefined`
4. 泛型函数:`function foo<T>(value: T): T`
5. 泛型约束:`<T extends BaseType>`
注意:在当前测试集中,泛型主要用于类型定义和类型别名,较少用于运行时代码。
#### 1.5.2 泛型类型别名
泛型类型别名使用类型参数定义可复用的类型模板。
在测试集中,泛型类型别名主要用于:
```typescript
// enums.d.ts - 简单泛型类型别名
type Nullable<T> = T | undefined;
// common.d.ts - 泛型类型别名
type Optional<T> = T | undefined;
```
泛型类型别名在 Cangjie 中不直接生成代码,而是在使用时展开为具体类型:
```typescript
// ArkTS 定义
type Nullable<T> = T | undefined;
property: Nullable<string>;
// Cangjie 翻译时展开
// property: string | undefined → property: Option<String>
```
转换规则:
- 泛型类型别名不生成独立的 Cangjie 类型定义
- 使用时展开为具体类型,再按照相应的类型映射规则转换
- `T | undefined` 模式转换为 `Option<T>`
#### 1.5.3 泛型接口
泛型接口使用类型参数定义可复用的接口模板。
在测试集中,泛型接口主要用于:
```typescript
// common.d.ts - 泛型接口定义
declare interface AnimatableArithmetic<T> {
plus(rhs: AnimatableArithmetic<T>): AnimatableArithmetic<T>;
subtract(rhs: AnimatableArithmetic<T>): AnimatableArithmetic<T>;
multiply(scale: number): AnimatableArithmetic<T>;
equals(rhs: AnimatableArithmetic<T>): boolean;
}
// common.d.ts - 泛型接口用于属性修改器
declare interface AttributeModifier<T> {
applyNormalAttribute?(instance: T): void;
}
declare interface ContentModifier<T> {
applyContent(): WrappedBuilder<[T]>;
}
// units.d.ts - 泛型接口用于边距类型
declare interface DirectionalEdgesT<T> {
start?: T;
end?: T;
top?: T;
bottom?: T;
}
```
泛型接口在 Cangjie 中不生成泛型代码,原因如下:
1. 这些接口主要用于框架内部的类型约束
2. 在实际使用时,类型参数已经具体化
3. 当前测试集中这些泛型接口未被实际使用
转换规则:
- 泛型接口定义通常不生成 Cangjie 代码
- 如果被具体使用,则生成具体化后的接口定义
- 例如:`AttributeModifier<ButtonAttribute>` 会生成具体的 `AttributeModifier_ButtonAttribute` 类
#### 1.5.4 泛型类(CRTP 模式)
泛型类使用类型参数定义可复用的类模板。在 ArkTS 组件系统中,广泛使用 CRTP(Curiously Recurring Template Pattern)模式实现链式调用。
在测试集中,泛型类主要用于组件基类:
```typescript
// common.d.ts - 泛型基类(CRTP 模式)
declare class CommonMethod<T> {
width(widthValue: Length | LayoutPolicy | undefined): T;
height(heightValue: Length | LayoutPolicy | undefined): T;
backgroundColor(value: ResourceColor | undefined): T;
onClick(event: ((event?: ClickEvent) => void) | undefined): T;
// ... 350+ 方法,都返回 T
}
// button.d.ts - 具体组件继承泛型基类
declare class ButtonAttribute extends CommonMethod<ButtonAttribute> {
type(value: ButtonType | undefined): ButtonAttribute;
stateEffect(value: boolean | undefined): ButtonAttribute;
fontSize(value: Length | undefined): ButtonAttribute;
// ... 组件特有方法
}
// text.d.ts - 另一个具体组件
declare class TextAttribute extends CommonMethod<TextAttribute> {
fontColor(value: ResourceColor | undefined): TextAttribute;
fontSize(value: Length | undefined): TextAttribute;
fontWeight(value: number | string | FontWeight | undefined): TextAttribute;
// ... 组件特有方法
}
```
CRTP 模式说明:
CRTP 是一种设计模式,子类将自己作为类型参数传递给父类:
- `class ButtonAttribute extends CommonMethod<ButtonAttribute>`
- 这样父类方法返回 `T`(即 `ButtonAttribute`),实现类型安全的链式调用
- 每个方法返回子类类型,支持连续调用子类和父类方法
虽然 Cangjie 支持泛型类(如 `class Base<T> { ... }`),但当前项目选择采用接口 + 继承 + `This` 类型的方式实现 CRTP 模式,而非使用泛型:
```cangjie
// CommonMethod 接口(定义方法签名)
public interface CommonMethod {
func width(widthValue: Option<Length>): This
func width(widthValue: Option<LayoutPolicy>): This
func height(heightValue: Option<Length>): This
func height(heightValue: Option<LayoutPolicy>): This
func backgroundColor(value: Option<ResourceColor>): This
func onClick(event: Option<((event: Option<ClickEvent>) -> Unit)>): This
// ... 350+ 方法
}
// ArkCommonMethodComponent 基类(实现 CommonMethod 接口)
public open class ArkCommonMethodComponent <: ComponentBase & CommonMethod {
protected open func getPeer(): ArkCommonMethodPeer { ... }
public func width(widthValue: Option<Length>): This {
this.getPeer().widthAttribute(widthValue)
return this // 返回 This(当前实例类型)
}
public func height(heightValue: Option<Length>): This {
this.getPeer().heightAttribute(heightValue)
return this
}
// ... 实现所有 CommonMethod 方法
}
// ArkButtonComponent 具体组件(继承基类)
public open class ArkButtonComponent <: ArkCommonMethodComponent {
protected func getPeer(): ArkButtonPeer { ... }
public func type_(value: Option<ButtonType>): This {
this.getPeer().typeAttribute(value)
return this
}
public func stateEffect(value: Option<Bool>): This {
this.getPeer().stateEffectAttribute(value)
return this
}
// ... 组件特有方法
}
```
转换规则如下:
| ArkTS 泛型类 | Cangjie 实现 | 说明 |
|-------------|-------------|------|
| `class CommonMethod<T>` | `interface CommonMethod` | 泛型类转换为接口 |
| `T` 类型参数 | `This` 类型 | 使用 `This` 表示当前实例类型 |
| `extends CommonMethod<ButtonAttribute>` | `<: ArkCommonMethodComponent` | 继承基类实现 |
| 方法返回 `T` | 方法返回 `This` | 支持链式调用 |
**链式调用示例**:
```typescript
// ArkTS 链式调用
Button("Click")
.width(100) // CommonMethod<ButtonAttribute> 方法
.height(50) // CommonMethod<ButtonAttribute> 方法
.type(ButtonType.Capsule) // ButtonAttribute 方法
.fontSize(16) // ButtonAttribute 方法
.onClick(() => { ... }) // CommonMethod<ButtonAttribute> 方法
```
```cangjie
// Cangjie 链式调用
ArkButton.create()
.setButtonOptions("Click", Option.None)
.width(Option.Some(Length.vp(100.0))) // ArkCommonMethodComponent 方法
.height(Option.Some(Length.vp(50.0))) // ArkCommonMethodComponent 方法
.type_(Option.Some(ButtonType.Capsule)) // ArkButtonComponent 方法
.fontSize(Option.Some(Length.fp(16.0))) // ArkButtonComponent 方法
.onClick(Option.Some({ ... })) // ArkCommonMethodComponent 方法
```
说明:
- 所有方法都返回 `This`,保持当前实例类型
- 可以连续调用父类方法和子类方法
- 类型安全:编译器知道每个方法返回的具体类型
#### 1.5.5 泛型函数
泛型函数使用类型参数定义可复用的函数模板。
在测试集中,泛型函数使用较少:
```typescript
// common.d.ts - 泛型函数
declare function applyStyles<T extends CommonMethod>(
self: T,
customStyles: CustomStyles
): T;
```
泛型函数在 Cangjie 中不生成代码,原因如下:
1. 这些函数主要用于框架内部
2. 当前测试集专注于类型定义,不包含函数实现
转换规则:
- 泛型函数定义通常不生成 Cangjie 代码
- 如果需要实现,则生成具体化后的函数版本
#### 1.5.6 泛型处理总结
测试集统计:
根据 `pay-subset-no-gesture` 测试集统计:
- 泛型类(CRTP 模式):广泛使用,已完整支持
- `CommonMethod<T>` → `interface CommonMethod` + `This` 类型
- 泛型类型别名:约 2 次使用,使用时展开
- `Nullable<T>` 不生成独立定义
- 泛型接口:约 5 次使用,未实际使用
- `AttributeModifier<T>` 等未生成代码
- 泛型函数:约 1 次使用,未生成代码
转换策略总结:
| 泛型类型 | ArkTS 语法 | Cangjie 转换策略 | 状态 |
|---------|-----------|-----------------|------|
| 泛型类(CRTP) | `class Base<T>` | 接口 + `This` 类型 | ✅ 已实现 |
| 泛型类型别名 | `type Foo<T> = ...` | 使用时展开 | ✅ 已实现 |
| 泛型接口 | `interface Foo<T>` | 不生成或具体化 | ⚠️ 按需生成 |
| 泛型函数 | `function foo<T>()` | 不生成或具体化 | ⚠️ 按需生成 |
注意事项:
1. Cangjie 支持泛型,但项目选择不使用:
- Cangjie 语言本身支持泛型类、泛型接口和泛型函数
- 语法示例:`class List<T> { ... }`、`interface Eq<T> { ... }`、`func foo<T>(x: T) { ... }`
- 但当前项目选择不使用泛型特性,而是采用 `This` 类型和接口模式实现链式调用
- 原因:简化代码生成逻辑,避免泛型实例化的复杂性
2. CRTP 模式转换:
- ArkTS 的 `class Child extends Base<Child>` 模式
- 转换为 Cangjie 的接口 + 继承 + `This` 类型(而非泛型类)
3. This 类型:
- Cangjie 的 `This` 类型表示当前实例的实际类型
- 支持链式调用时保持类型安全
4. 类型展开:
- 泛型类型别名在使用时展开为具体类型
- `Nullable<string>` → `string | undefined` → `Option<String>`
5. 按需生成:
- 未被实际使用的泛型定义不生成代码
- 只有在具体使用时才生成具体化的版本
6. 接口方法签名:
- `CommonMethod` 接口定义所有方法签名
- 返回类型统一为 `This`
- 基类和子类都实现该接口
### 1.6 枚举(Enum)
#### 1.6.1 枚举概述
枚举(Enum)是一种特殊的数据类型,用于定义一组命名的常量值。在 ArkTS 中,枚举有两种类型:
1. 数值枚举:枚举成员的值是数字(`enum { A = 0, B = 1 }`)
2. 字符串枚举:枚举成员的值是字符串(`enum { A = 'a', B = 'b' }`)
枚举的特点:
- 类型安全:枚举值只能是定义的成员之一
- 可读性强:使用语义化的名称代替魔法数字/字符串
- 双向映射:数值枚举支持从值到名称的反向查找
#### 1.6.2 数值枚举(有默认值)
数值枚举的成员显式指定为数字类型,每个成员都有一个对应的数值。
在测试集中,数值枚举广泛使用:
```typescript
// enums.d.ts - 数值枚举(从 0 开始)
declare enum CheckBoxShape {
CIRCLE = 0,
ROUNDED_SQUARE = 1
}
// button.d.ts - 数值枚举
declare enum ButtonType {
CAPSULE = 0,
CIRCLE = 1,
NORMAL = 2,
ROUNDED_RECTANGLE = 3
}
declare enum ButtonRole {
NORMAL = 0,
ERROR = 1
}
// grid.d.ts - 数值枚举
declare enum GridItemAlignment {
DEFAULT = 0,
STRETCH = 1
}
// styled_string.d.ts - 数值枚举(不连续的值)
declare enum StyledStringKey {
FONT = 0,
DECORATION = 1,
BASELINE_OFFSET = 2,
LETTER_SPACING = 3,
TEXT_SHADOW = 4,
LINE_HEIGHT = 5,
BACKGROUND_COLOR = 6,
URL = 7,
PARAGRAPH_STYLE = 200, // 跳跃值
IMAGE = 300,
CUSTOM_SPAN = 400,
USER_DATA = 500
}
```
转换后的代码:
数值枚举转换为 Cangjie 的枚举类型,并生成辅助方法:
```cangjie
// CheckBoxShape 枚举翻译
public enum CheckBoxShape <: ToString & Equatable<CheckBoxShape> {
| CIRCLE
| ROUNDED_SQUARE
// 获取枚举值的数值表示
public func get(): Int32 {
match(this) {
case CIRCLE => 0
case ROUNDED_SQUARE => 1
}
}
// 从数值解析为枚举
static func parse(val: Int32): CheckBoxShape {
match(val) {
case 0 => CIRCLE
case 1 => ROUNDED_SQUARE
case _ => throw IllegalArgumentException("unknown value ${val}")
}
}
// 安全解析(返回 Option)
static func tryParse(val: ?Int32): ?CheckBoxShape {
match(val) {
case Some(v) => parse(v)
case None => None
}
}
// ToString 接口实现
public func toString(): String {
match(this) {
case CIRCLE => "Circle"
case ROUNDED_SQUARE => "Rounded_square"
}
}
// Equatable 接口实现
public override operator func ==(that: CheckBoxShape): Bool {
match((this, that)) {
case(CIRCLE, CIRCLE) => true
case(ROUNDED_SQUARE, ROUNDED_SQUARE) => true
case _ => false
}
}
public override operator func !=(that: CheckBoxShape): Bool {
!(this == that)
}
}
// ButtonType 枚举翻译(4 个成员)
public enum ButtonType <: ToString & Equatable<ButtonType> {
| CAPSULE
| CIRCLE
| NORMAL
| ROUNDED_RECTANGLE
public func get(): Int32 {
match(this) {
case CAPSULE => 0
case CIRCLE => 1
case NORMAL => 2
case ROUNDED_RECTANGLE => 3
}
}
static func parse(val: Int32): ButtonType {
match(val) {
case 0 => CAPSULE
case 1 => CIRCLE
case 2 => NORMAL
case 3 => ROUNDED_RECTANGLE
case _ => throw IllegalArgumentException("unknown value ${val}")
}
}
static func tryParse(val: ?Int32): ?ButtonType {
match(val) {
case Some(v) => parse(v)
case None => None
}
}
public func toString(): String {
match(this) {
case CAPSULE => "Capsule"
case CIRCLE => "Circle"
case NORMAL => "Normal"
case ROUNDED_RECTANGLE => "Rounded_rectangle"
}
}
public override operator func ==(that: ButtonType): Bool {
match((this, that)) {
case(CAPSULE, CAPSULE) => true
case(CIRCLE, CIRCLE) => true
case(NORMAL, NORMAL) => true
case(ROUNDED_RECTANGLE, ROUNDED_RECTANGLE) => true
case _ => false
}
}
public override operator func !=(that: ButtonType): Bool {
!(this == that)
}
}
```
转换规则如下:
| ArkTS 语法 | Cangjie 语法 | 说明 |
|-----------|-------------|------|
| `enum Name { ... }` | `enum Name <: ToString & Equatable<Name> { ... }` | 枚举定义,实现 ToString 和 Equatable 接口 |
| `MEMBER = 0` | `\| MEMBER` | 枚举成员,使用竖线分隔 |
| 成员访问:`Name.MEMBER` | `Name.MEMBER` | 访问方式相同 |
| 数值获取:无直接语法 | `member.get(): Int32` | 生成 get() 方法获取数值 |
| 反向解析:无直接语法 | `Name.parse(val: Int32)` | 生成静态方法从数值解析 |
| 安全解析:无直接语法 | `Name.tryParse(val: ?Int32)` | 生成安全解析方法 |
#### 1.6.3 字符串枚举(字符串默认值)
字符串枚举的成员显式指定为字符串类型。
在测试集中,字符串枚举主要用于颜色定义:
```typescript
// enums.d.ts - 字符串枚举
declare enum Color {
White = '#FFFFFFFF',
Black = '#FF000000',
Blue = '#FF0000FF',
Brown = '#FFA52A2A',
Gray = '#FF808080',
Green = '#FF008000',
Grey = '#FF808080',
Orange = '#FFFFA500',
Pink = '#FFFFC0CB',
Red = '#FFFF0000',
Yellow = '#FFFFFF00',
Transparent = '#00000000'
}
```
转换后的代码:
字符串枚举转换为 Cangjie 枚举,并生成双重解析方法(按索引和按值):
```cangjie
public enum Color <: ToString & Equatable<Color> {
| WHITE
| BLACK
| BLUE
| BROWN
| GRAY
| GREEN
| GREY
| ORANGE
| PINK
| RED
| YELLOW
| TRANSPARENT
// 获取枚举的索引值(0-based)
public func get(): Int32 {
match(this) {
case WHITE => 0
case BLACK => 1
case BLUE => 2
case BROWN => 3
case GRAY => 4
case GREEN => 5
case GREY => 6
case ORANGE => 7
case PINK => 8
case RED => 9
case YELLOW => 10
case TRANSPARENT => 11
}
}
// 获取枚举的字符串值
public func getValue(): String {
match(this) {
case WHITE => "#FFFFFFFF"
case BLACK => "#FF000000"
case BLUE => "#FF0000FF"
case BROWN => "#FFA52A2A"
case GRAY => "#FF808080"
case GREEN => "#FF008000"
case GREY => "#FF808080"
case ORANGE => "#FFFFA500"
case PINK => "#FFFFC0CB"
case RED => "#FFFF0000"
case YELLOW => "#FFFFFF00"
case TRANSPARENT => "#00000000"
}
}
// 从索引解析为枚举
static func parse(val: Int32): Color {
match(val) {
case 0 => WHITE
case 1 => BLACK
case 2 => BLUE
case 3 => BROWN
case 4 => GRAY
case 5 => GREEN
case 6 => GREY
case 7 => ORANGE
case 8 => PINK
case 9 => RED
case 10 => YELLOW
case 11 => TRANSPARENT
case _ => throw IllegalArgumentException("unknown value ${val}")
}
}
static func tryParse(val: ?Int32): ?Color {
match(val) {
case Some(v) => parse(v)
case None => None
}
}
// 从字符串值解析为枚举
static func parse(val: String): Color {
match(val) {
case "#FFFFFFFF" => WHITE
case "#FF000000" => BLACK
case "#FF0000FF" => BLUE
case "#FFA52A2A" => BROWN
case "#FF808080" => GRAY
case "#FF008000" => GREEN
// GREY 和 GRAY 值相同,按第一个匹配
case "#FFFFA500" => ORANGE
case "#FFFFC0CB" => PINK
case "#FFFF0000" => RED
case "#FFFFFF00" => YELLOW
case "#00000000" => TRANSPARENT
case _ => throw IllegalArgumentException("unknown value ${val}")
}
}
static func tryParse(val: ?String): ?Color {
match(val) {
case Some(v) => parse(v)
case None => None
}
}
public func toString(): String {
match(this) {
case WHITE => "White"
case BLACK => "Black"
case BLUE => "Blue"
case BROWN => "Brown"
case GRAY => "Gray"
case GREEN => "Green"
case GREY => "Grey"
case ORANGE => "Orange"
case PINK => "Pink"
case RED => "Red"
case YELLOW => "Yellow"
case TRANSPARENT => "Transparent"
}
}
public override operator func ==(that: Color): Bool {
match((this, that)) {
case(WHITE, WHITE) => true
case(BLACK, BLACK) => true
case(BLUE, BLUE) => true
// ... 其他成员
case _ => false
}
}
public override operator func !=(that: Color): Bool {
!(this == that)
}
}
```
转换规则如下:
| ArkTS 语法 | Cangjie 语法 | 说明 |
|-----------|-------------|------|
| `enum Name { A = 'a' }` | `enum Name { \| A }` | 枚举定义 |
| 成员值:`A = '#FFF'` | `member.getValue(): String` | 生成 getValue() 方法获取字符串值 |
| 反向解析:无 | `Name.parse(val: String)` | 生成从字符串值解析的方法 |
| 索引值:无 | `member.get(): Int32` | 额外生成索引值方法(0-based) |
说明:
- 字符串枚举同时支持**按索引**和**按字符串值**两种解析方式
- `get()` 返回枚举成员的索引(0, 1, 2, ...)
- `getValue()` 返回枚举成员的字符串值(如 `#FFFFFFFF`)
#### 1.6.4 枚举成员命名规则
**ArkTS vs Cangjie 命名转换**:
| ArkTS 命名 | Cangjie 命名 | 说明 |
|-----------|-------------|------|
| `PascalCase` | `UPPER_CASE` | ArkTS 的 PascalCase 转为 Cangjie 的 UPPER_CASE |
| `UPPER_CASE` | `UPPER_CASE` | 已是 UPPER_CASE 则保持不变 |
| `Capsule` | `CAPSULE` | 单词首字母大写转为全大写 |
| `White` | `WHITE` | 单词首字母大写转为全大写 |
| `RoundedRectangle` | `ROUNDED_RECTANGLE` | 驼峰命名转为下划线分隔的大写 |
**示例**:
```typescript
// ArkTS
declare enum ButtonType {
Capsule = 0, // PascalCase
CIRCLE = 1, // UPPER_CASE
Normal = 2, // PascalCase
RoundedRectangle = 3 // PascalCase (多词)
}
```
```cangjie
// Cangjie
public enum ButtonType {
| CAPSULE // 转为全大写
| CIRCLE // 保持不变
| NORMAL // 转为全大写
| ROUNDED_RECTANGLE // 驼峰转下划线大写
}
```
#### 1.6.5 枚举辅助方法总结
**生成的辅助方法**:
所有枚举都会生成以下辅助方法:
1. **get(): Int32** - 获取枚举成员的序号/数值
- 数值枚举:返回显式指定的数值
- 字符串枚举:返回成员的索引(0-based)
2. **getValue(): String** - 获取字符串值(仅字符串枚举)
- 返回枚举成员定义的字符串值
3. **parse(val: Int32): EnumType** - 从数值解析枚举
- 失败时抛出 `IllegalArgumentException`
4. **tryParse(val: ?Int32): ?EnumType** - 安全解析
- 失败时返回 `None`
5. **parse(val: String): EnumType** - 从字符串解析(仅字符串枚举)
- 失败时抛出异常
6. **tryParse(val: ?String): ?EnumType** - 安全解析字符串(仅字符串枚举)
7. **toString(): String** - 转为字符串表示
- 返回枚举成员的名称(PascalCase 格式)
8. **operator ==(that: EnumType): Bool** - 相等性比较
9. **operator !=(that: EnumType): Bool** - 不等性比较
**接口实现**:
所有枚举都实现两个接口:
- `ToString` - 提供 `toString()` 方法
- `Equatable<T>` - 提供 `==` 和 `!=` 运算符
#### 1.6.6 枚举翻译规则总述
**测试集统计**:
根据 `pay-subset-no-gesture` 测试集统计:
- 数值枚举:约 50+ 个(最常用)
- 字符串枚举:约 2 个(`Color` 等)
- 所有枚举都生成完整的辅助方法
**转换策略总结**:
| 特性 | ArkTS | Cangjie | 说明 |
|------|-------|---------|------|
| 枚举定义 | `enum Name { ... }` | `enum Name <: ToString & Equatable<Name> { ... }` | 实现接口 |
| 成员定义 | `MEMBER = 0` | `\| MEMBER` | 使用竖线分隔 |
| 成员命名 | PascalCase | UPPER_CASE | 命名风格转换 |
| 数值获取 | `Name.MEMBER` 直接是数值 | `Name.MEMBER.get()` | 需要调用方法 |
| 字符串值 | `Name.MEMBER` 直接是字符串 | `Name.MEMBER.getValue()` | 字符串枚举特有 |
| 反向解析 | 无标准方式 | `Name.parse(val)` | 生成静态方法 |
| 类型安全 | ✅ 编译时检查 | ✅ 编译时检查 | 都支持 |
| 模式匹配 | ❌ 不支持 | ✅ 支持 | Cangjie 支持 match |
**注意事项**:
1. **命名转换**:所有枚举成员名称转为 UPPER_CASE 格式
2. **辅助方法生成**:每个枚举自动生成 9 个辅助方法
3. **双重解析**:字符串枚举支持按索引和按值两种解析方式
4. **接口实现**:所有枚举实现 `ToString` 和 `Equatable` 接口
5. **模式匹配**:Cangjie 枚举支持 `match` 表达式进行模式匹配
6. **序列化**:枚举序列化时使用 `get()` 方法获取数值,详见《ArkTS-Cangjie 序列化规则》
### 1.7 联合类型(Union Types)
#### 1.7.1 联合类型概述
**ArkTS 类型说明**:
联合类型(Union Type)表示一个值可以是几种类型之一,使用 `|` 符号连接多个类型。联合类型提供了类型的灵活性,同时保持类型安全。
联合类型的常见形式:
- **基础类型联合**:`string | number`
- **枚举联合**:`FontWeight | string`
- **复杂类型联合**:`string | Resource | double`
- **包含 undefined**:`Type | undefined`
**重要区分**:
联合类型在不同位置的处理方式**完全不同**:
1. **属性中的联合类型**:使用 **Selector 模式**生成联合类型类
2. **函数参数中的联合类型**:使用 **方法重载**生成多个方法
3. **Type | undefined**:特殊处理,转换为 `Option<Type>`
本节将详细说明这三种情况的处理规则。
#### 1.7.2 属性中的联合类型(Selector 模式)
当接口属性、类型别名等使用联合类型时,表示该属性可以是多种类型之一。需要区分两种情况:
1. **通过 type 别名定义的联合类型**:`type Length = string | double | Resource`
2. **直接在属性中声明的联合类型**:`property?: string | Resource`
在测试集中,属性联合类型广泛使用:
**情况1:通过 type 别名定义的联合类型**
```typescript
// units.d.ts - 类型别名定义
type Length = string | double | Resource;
type ResourceStr = string | Resource;
type ResourceColor = Color | number | string | Resource;
type Dimension = PX | VP | FP | LPX | Percentage | Resource;
// button.d.ts - 接口属性使用联合类型别名
declare interface ButtonLabelStyle {
overflow?: TextOverflow;
maxLines?: int;
minFontSize?: Length; // Length 是联合类型别名
maxFontSize?: Length; // Length 是联合类型别名
heightAdaptivePolicy?: TextHeightAdaptivePolicy;
font?: Font;
}
```
**情况2:直接在属性中声明的联合类型**
```typescript
// units.d.ts - 接口属性直接使用联合类型
declare interface Font {
size?: Length; // 类型别名 → 使用 Selector 模式
weight?: FontWeight | number | string; // 直接联合类型 → 只取第一种类型
family?: string | Resource; // 直接联合类型 → 只取第一种类型
style?: FontStyle;
}
// list.d.ts - 直接联合类型
declare interface ListOptions {
space?: double | string; // 直接联合类型 → 只取第一种类型(double)
}
// column.d.ts - 直接联合类型
declare interface ColumnOptions {
space?: string | number; // 直接联合类型 → 只取第一种类型(string)
}
```
转换后的代码:
属性中的联合类型有两种处理方式:
**方式1:通过 type 别名定义的联合类型 → Selector 类**
当属性使用通过 `type` 定义的联合类型别名时,生成 Selector 类:
```cangjie
// Length 联合类型生成 Selector 类
public class Length {
private var selector: Int32
public func getSelector(): Int32 {
return selector
}
// 分支 0: string
private var value0: Option<String> = None<String>
public init(param: String) {
value0 = param
selector = 0
}
public func getValue0(): String {
match (value0) {
case Some(x) => return x
case None => throw Exception("Wrong selector value inside Union Length")
}
}
// 分支 1: Float64
private var value1: Option<Float64> = None<Float64>
public init(param: Float64) {
value1 = param
selector = 1
}
public func getValue1(): Float64 {
match (value1) {
case Some(x) => return x
case None => throw Exception("Wrong selector value inside Union Length")
}
}
// 分支 2: Resource
private var value2: Option<Resource> = None<Resource>
public init(param: Resource) {
value2 = param
selector = 2
}
public func getValue2(): Resource {
match (value2) {
case Some(x) => return x
case None => throw Exception("Wrong selector value inside Union Length")
}
}
}
```
**Selector 模式说明**:
1. **selector 字段**:整数值,表示当前实际使用的类型分支(0, 1, 2, ...)
2. **多个构造器**:每个类型分支一个构造器
- `init(param: String)` - 设置 selector = 0
- `init(param: Float64)` - 设置 selector = 1
- `init(param: Resource)` - 设置 selector = 2
3. **valueN 字段**:每个分支一个 Option 字段
- `value0: Option<String>` - 存储 string 值
- `value1: Option<Float64>` - 存储 double 值
- `value2: Option<Resource>` - 存储 Resource 值
4. **getValueN() 方法**:安全获取对应分支的值
- 如果 selector 不匹配,抛出异常
**方式2:直接在属性中声明的联合类型 → 只取第一种类型**
当属性直接使用联合类型(未通过 type 别名)时,**只支持第一种类型**:
```cangjie
// Font 类翻译
public class Font {
// size 使用类型别名 Length → Selector 类
public var size: Option<Length> // Length 是 Selector 类
// weight 直接联合类型 → 只取第一种类型 FontWeight
public var weight: Option<FontWeight> // 只支持 FontWeight,不支持 number 和 string
// family 直接联合类型 → 只取第一种类型 string
public var family: Option<String> // 只支持 String,不支持 Resource
// style 单一类型
public var style: Option<FontStyle>
public init(
size: Option<Length>,
weight: Option<FontWeight>,
family: Option<String>,
style: Option<FontStyle>
) {
this.size = size
this.weight = weight
this.family = family
this.style = style
}
}
// ListOptions 类翻译
public class ListOptions {
// space 直接联合类型 → 只取第一种类型 double
public var space: Option<Float64> // 只支持 Float64,不支持 String
public init(space: Option<Float64>, ...) {
this.space = space
// ...
}
}
```
**使用示例**:
```cangjie
// 创建 Length 实例(Selector 类)
let len1 = Length("12vp") // selector = 0
let len2 = Length(100.0) // selector = 1
let len3 = Length(Resource(...)) // selector = 2
// 获取值(需要知道当前分支)
let selector = len1.getSelector()
if (selector == 0) {
let strValue = len1.getValue0() // "12vp"
} else if (selector == 1) {
let numValue = len1.getValue1() // 会抛异常,因为 selector != 1
}
// 创建 Font 实例(直接联合类型只支持第一种)
let font = Font(
size: Option.Some(Length("16fp")),
weight: Option.Some(FontWeight.Normal), // ✅ 支持 FontWeight
// weight: Option.Some(400) // ❌ 不支持 number
// weight: Option.Some("normal") // ❌ 不支持 string
family: Option.Some("Arial"), // ✅ 支持 String
// family: Option.Some(Resource(...)) // ❌ 不支持 Resource
style: Option.Some(FontStyle.Normal)
)
```
转换规则如下:
| ArkTS 语法 | Cangjie 转换 | 说明 |
|-----------|-------------|------|
| `type T = A \| B \| C` | `class T { selector, valueN, ... }` | 生成 Selector 类 |
| `prop?: T`(T 是类型别名) | `prop: Option<T>` | 使用 Selector 类 |
| `prop?: A \| B \| C`(直接联合) | `prop: Option<A>` | **只取第一种类型** |
| `prop?: string \| Resource` | `prop: Option<String>` | 只支持 string |
| `prop?: FontWeight \| number \| string` | `prop: Option<FontWeight>` | 只支持 FontWeight |
**限制说明**:
1. **类型别名 → 完整支持**:通过 `type` 定义的联合类型生成 Selector 类,支持所有分支
2. **直接联合类型 → 只支持第一种**:
- `family?: string | Resource` → 只支持 `string`
- `weight?: FontWeight | number | string` → 只支持 `FontWeight`
- `space?: double | string` → 只支持 `double`
3. **设计原因**:
- 类型别名可以提前定义 Selector 类
- 直接联合类型需要内联生成,为了简化实现,只取第一种类型
- 如果需要完整支持,应该先定义类型别名
#### 1.7.3 函数参数中的联合类型(重载)
当函数参数使用联合类型时,表示参数可以接受多种类型。
在测试集中,函数参数联合类型常见于组件方法:
```typescript
// button.d.ts - 方法参数使用联合类型
declare class ButtonAttribute extends CommonMethod<ButtonAttribute> {
// fontSize 参数是 Length 联合类型
fontSize(value: Length | undefined): ButtonAttribute;
// fontWeight 参数是三元联合类型
fontWeight(value: number | FontWeight | string | undefined): ButtonAttribute;
// fontColor 参数是 ResourceColor 联合类型
fontColor(value: ResourceColor | undefined): ButtonAttribute;
}
// text.d.ts - 方法参数使用联合类型
declare class TextAttribute extends CommonMethod<TextAttribute> {
// fontSize 参数是四元联合类型
fontSize(value: double | string | Resource | undefined): TextAttribute;
// fontWeight 参数是三元联合类型
fontWeight(weight: number | FontWeight | string | undefined,
options?: FontSettingOptions | undefined): TextAttribute;
}
```
转换后的代码:
函数参数中的联合类型转换为 **方法重载**,为每个类型分支生成一个独立方法:
```cangjie
// ButtonAttribute 类中的重载方法
public open class ArkButtonComponent <: ArkCommonMethodComponent {
// fontSize: Length | undefined
// Length = string | double | Resource,只取联合类型本身,不展开
public func fontSize(value: Option<Length>): This {
this.getPeer().fontSizeAttribute(value)
return this
}
// fontWeight: number | FontWeight | string | undefined
// 生成 3 个重载方法(number 映射为 Float64)
public func fontWeight(value: Option<Float64>): This {
this.getPeer().fontWeightAttribute(value)
return this
}
public func fontWeight(value: Option<FontWeight>): This {
this.getPeer().fontWeightAttribute(value)
return this
}
public func fontWeight(value: Option<String>): This {
this.getPeer().fontWeightAttribute(value)
return this
}
}
```
**重载规则说明**:
1. **类型别名不展开**:
- `fontSize(value: Length | undefined)`
- → 只生成 1 个方法:`fontSize(value: Option<Length>)`
- 不会展开为 3 个方法(string、double、Resource)
2. **直接联合类型展开**:
- `fontWeight(value: number | FontWeight | string | undefined)`
- → 生成 3 个重载方法,每个类型一个
3. **undefined 转换**:
- 所有参数的 `| undefined` 统一转换为 `Option<Type>`
转换规则如下:
| ArkTS 参数类型 | Cangjie 方法重载 | 说明 |
|---------------|-----------------|------|
| `param: A \| B \| undefined` | `func(param: Option<A>)` + `func(param: Option<B>)` | 生成 2 个重载 |
| `param: TypeAlias \| undefined` | `func(param: Option<TypeAlias>)` | 类型别名不展开 |
| `param: Length \| undefined` | `func(param: Option<Length>)` | Length 保持为联合类型类 |
| `param: A \| B \| C \| undefined` | 生成 3 个重载 | 每个类型一个方法 |
**为什么属性和参数处理不同?**
1. **属性**:需要存储值,必须知道实际类型 → 使用 Selector 存储
2. **参数**:只需要传递值,类型在调用时已知 → 使用重载提供多种签名
#### 1.7.4 `Type | undefined` 特殊情况
`Type | undefined` 是最常见的联合类型,表示值可能不存在。需要区分两种情况:
1. 可选属性语法:`property?: Type`(在 1.2 节处理)
2. 联合类型语法:`property: Type | undefined`
**测试集中的用法现场**:
```typescript
// button.d.ts - 方法参数:Type | undefined
declare class ButtonAttribute {
fontSize(value: Length | undefined): ButtonAttribute;
fontWeight(value: number | FontWeight | string | undefined): ButtonAttribute;
type(value: ButtonType | undefined): ButtonAttribute;
}
// common.d.ts - 可选属性(1.2 节已处理)
declare interface ButtonOptions {
type?: ButtonType; // 可选属性语法
stateEffect?: boolean; // 可选属性语法
}
// text_common.d.ts - 属性:Type | undefined
declare interface TextDataDetectorConfig {
types: TextDataDetectorType[] | undefined | null; // 显式联合类型
}
```
转换后的代码:
`Type | undefined` 统一转换为 `Option<Type>`:
```cangjie
// 方法参数:Type | undefined
public func fontSize(value: Option<Length>): This {
this.getPeer().fontSizeAttribute(value)
return this
}
public func type_(value: Option<ButtonType>): This {
this.getPeer().typeAttribute(value)
return this
}
// 可选属性:property?:Type(1.2 节)
public class ButtonOptions {
public var type_: Option<ButtonType>
public var stateEffect: Option<Bool>
// ...
}
// 属性:Type | undefined | null
public class TextDataDetectorConfig {
public var types: Option<ArrayList<TextDataDetectorType>>
// null 也映射为 Option.None
}
```
**转换规则总结**:
| ArkTS 语法 | Cangjie 类型 | 说明 |
|-----------|-------------|------|
| `property?: Type` | `property: Option<Type>` | 可选属性(1.2 节) |
| `property: Type \| undefined` | `property: Option<Type>` | 显式联合 undefined |
| `param: Type \| undefined` | `param: Option<Type>` | 参数联合 undefined |
| `Type \| undefined \| null` | `Option<Type>` | null 也映射为 None |
说明:
- `property?: Type` 和 `property: Type | undefined` 在 Cangjie 中翻译结果相同
- 都转换为 `Option<Type>`
- `null` 和 `undefined` 都映射为 `Option.None`
#### 1.7.5 联合类型处理总结
**测试集统计**:
根据 `pay-subset-no-gesture` 测试集统计:
- 类型别名联合:约 10 个(`Length`、`ResourceStr`、`ResourceColor` 等)
- 属性联合类型:广泛使用,主要通过类型别名
- 参数联合类型:广泛使用,生成大量重载方法
- `Type | undefined`:最常用模式,统一转为 `Option<Type>`
**三种处理方式对比**:
| 使用位置 | ArkTS 语法 | Cangjie 转换 | 处理方式 |
|---------|-----------|-------------|---------|
| **类型别名** | `type T = A \| B` | `class T { selector, valueN }` | Selector 模式 |
| **属性类型** | `prop: A \| B` | `prop: Union_A_B` 或 Selector 类 | Selector 模式 |
| **参数类型** | `func(p: A \| B)` | `func(p: A)` + `func(p: B)` | 方法重载 |
| **类型别名参数** | `func(p: T)` | `func(p: T)` | 不展开 |
| **Type \| undefined** | `p?: T` 或 `p: T \| undefined` | `p: Option<T>` | Option 类型 |
**转换策略总结**:
1. **属性中的联合类型**:
- 生成 Selector 类
- 包含 selector 字段和多个 valueN 字段
- 每个分支一个构造器和 getter
2. **参数中的联合类型**:
- 生成方法重载
- 类型别名不展开
- 每个直接类型分支一个方法
3. **Type | undefined**:
- 统一转换为 `Option<Type>`
- 无论是 `?:` 语法还是 `| undefined` 语法
4. **嵌套联合类型**:
- `Array<string | int>` → Selector 类用于元素
- `(A | B) => void` → 回调参数使用 Selector 类
**注意事项**:
1. **类型别名引用不展开**:参数使用类型别名时,不展开为多个重载
2. **Selector 序列化**:序列化时需要检查 selector 值,详见《ArkTS-Cangjie 序列化规则》
3. **性能考虑**:Selector 类通过 Option 存储值,只有当前分支的 Option 为 Some
4. **类型安全**:调用 getValueN() 时如果 selector 不匹配会抛异常
5. **命名规则**:
- 类型别名保持原名:`Length`、`ResourceColor`
- 直接联合生成命名:`Union_A_B_C`
6. **可选联合类型**:`property?: A | B` 外层用 Option,内层用 Selector
- 类型:`Option<Union_A_B>`
### 1.8 回调函数类型
回调函数是 ArkTS 中用于事件处理和异步操作的重要机制。在组件属性和方法参数中广泛使用。
---
#### 1.8.1 回调类型定义翻译
使用 `type` 关键字定义回调函数类型别名,便于复用和类型检查:
```typescript
type CallbackName = (param1: Type1, param2: Type2, ...) => ReturnType;
```
在测试集中,回调函数类型别名广泛使用:
**1. 无参回调函数**
```typescript
// units.d.ts
type VoidCallback = () => void;
```
**2. 带参数的回调函数**
```typescript
// button.d.ts - 带两个参数
type ButtonTriggerClickCallback = (xPos: double, yPos: double) => void;
// text.d.ts - 带一个参数
type ImageErrorCallback = (error: ImageError) => void;
type ImageOnCompleteCallback = (loadEvent?: ImageCompleteEvent) => void;
// list.d.ts - 带多个参数
type OnScrollVisibleContentChangeCallback = (
start: VisibleListContentInfo,
end: VisibleListContentInfo
) => void;
// styled_string.d.ts - 带参数和返回值
type StyledStringMarshallCallback = (
marshallableVal: StyledStringMarshallingValue
) => ArrayBuffer;
type StyledStringUnmarshallCallback = (
buf: ArrayBuffer
) => StyledStringMarshallingValue;
```
**3. 返回元组的回调函数**
```typescript
// grid.d.ts - 返回二元组
onGetIrregularSizeByIndex?: (index: int) => [int, int];
// grid.d.ts - 返回四元组
onGetRectByIndex?: (index: int) => [int, int, int, int];
```
**4. 返回布尔值的回调函数**
```typescript
// list.d.ts
onItemMove(event: ((from: int, to: int) => boolean) | undefined): ListAttribute;
```
**5. 方法参数中的内联回调**
```typescript
// text.d.ts
onCopy(callback: ((value: string) => void) | undefined): TextAttribute;
onTextSelectionChange(
callback: ((selectionStart: int, selectionEnd: int) => void) | undefined
): TextAttribute;
// grid.d.ts
onScrollIndex(
event: ((first: int, last: int) => void) | undefined
): GridAttribute;
onItemDragMove(
event: ((event: ItemDragInfo, itemIndex: int, insertIndex: int) => void) | undefined
): GridAttribute;
// image.d.ts
onFinish(event: (() => void) | undefined): ImageAttribute;
// list.d.ts
onItemMove(event: ((from: int, to: int) => boolean) | undefined): ListAttribute;
```
**6. 接口属性中的回调**
```typescript
// list.d.ts
declare interface ScrollAnimationOptions {
duration?: number;
curve?: Curve | ICurve;
canOverScroll?: boolean;
onFinish?: () => void; // 回调属性
}
// grid.d.ts
declare interface GridLayoutOptions {
regularSize: [int, int];
irregularIndexes?: Array<int>;
onGetIrregularSizeByIndex?: (index: int) => [int, int];
onGetRectByIndex?: (index: int) => [int, int, int, int];
}
```
转换后的代码:
回调函数类型转换为 Cangjie 的 **函数类型**,使用 `->` 表示:
**1. 类型别名定义的回调 → `public type`**
```cangjie
// Common.cj
// 无参回调
public type VoidCallback = () -> Unit
public type Callback_Extender_OnFinish = () -> Unit
// 带参数回调
public type ButtonTriggerClickCallback = (xPos: Float64, yPos: Float64) -> Unit
public type SearchValueCallback = (value: String) -> Unit
public type Callback_RangeUpdate = (start: Int32, end: Int32) -> Unit
// 带可选参数
public type OnDragEventCallback = (event: DragEvent, extraParams: ?String) -> Unit
// 多参数复杂类型
public type ScrollOnScrollCallback = (
xOffset: Float64,
yOffset: Float64,
scrollState: ScrollState
) -> Unit
// 带返回值(非 void)
public type EditableTextOnChangeCallback = (
value: String,
previewText: ?PreviewText,
options: ?TextChangeOptions
) -> Unit
```
**2. 内联回调类型 → 直接使用函数类型**
```cangjie
// Component 层
public open class ArkTextComponent <: ArkCommonMethodComponent {
public func onCopy(callback_: Option<OnTextCopyCallback>): This {
this.getPeer().onCopyAttribute(callback_)
return this
}
}
// Peer 层 - 使用内联函数类型
public func onCopyAttribute(callback_: Option<((value: String) -> Unit)>): Unit {
let thisSerializer: SerializerBase = SerializerBase.hold()
if (let Some(callback_) <- callback_) {
thisSerializer.writeInt8(RuntimeType.OBJECT.ordinal)
let callback_TmpValue = callback_
thisSerializer.holdAndWriteCallback(callback_TmpValue)
} else {
thisSerializer.writeInt8(RuntimeType.UNDEFINED.ordinal)
}
ArkUIGeneratedNativeModule._TextAttribute_setOnCopy(
this.peer.ptr,
thisSerializer.asBuffer(),
thisSerializer.length()
)
thisSerializer.release()
}
```
**3. 返回元组的回调**
```cangjie
// ArkGridLayoutOptions.cj
public class GridLayoutOptions {
public var regularSize: (Int32, Int32)
public var irregularIndexes: Option<ArrayList<Int32>>
// 返回二元组的回调
public var onGetIrregularSizeByIndex: Option<((index: Int32) -> (Int32, Int32))>
// 返回四元组的回调
public var onGetRectByIndex: Option<((index: Int32) -> (Int32, Int32, Int32, Int32))>
public init(
regularSize: (Int32, Int32),
irregularIndexes: Option<ArrayList<Int32>>,
onGetIrregularSizeByIndex: Option<((index: Int32) -> (Int32, Int32))>,
onGetRectByIndex: Option<((index: Int32) -> (Int32, Int32, Int32, Int32))>
) {
this.regularSize = regularSize
this.irregularIndexes = irregularIndexes
this.onGetIrregularSizeByIndex = onGetIrregularSizeByIndex
this.onGetRectByIndex = onGetRectByIndex
}
}
```
**4. 可选回调处理**
```cangjie
// 可选回调属性 - 使用 Option 包装
public class GridLayoutOptions {
public var onGetIrregularSizeByIndex: Option<((index: Int32) -> (Int32, Int32))>
}
// 可选回调参数 - 使用 Option 包装
public func onClick(event: Option<OnCommonMethodClickCallback>): This { ... }
public func onFinish(event: Option<VoidCallback>): This { ... }
// Peer 层的可选回调处理
public func onClickAttribute(event: Option<((event: ClickEvent) -> Unit)>): Unit {
let thisSerializer: SerializerBase = SerializerBase.hold()
// 使用 if-let 模式匹配 Option
if (let Some(event) <- event) {
// 有回调:序列化为 OBJECT
thisSerializer.writeInt8(RuntimeType.OBJECT.ordinal)
let eventTmpValue = event
thisSerializer.holdAndWriteCallback(eventTmpValue)
} else {
// 无回调:序列化为 UNDEFINED
thisSerializer.writeInt8(RuntimeType.UNDEFINED.ordinal)
}
ArkUIGeneratedNativeModule._CommonMethod_setOnClick0(
this.peer.ptr,
thisSerializer.asBuffer(),
thisSerializer.length()
)
thisSerializer.release()
}
```
**转换规则总结**:
| ArkTS 语法 | Cangjie 转换 | 说明 |
|-----------|-------------|------|
| `type CB = () => void` | `public type CB = () -> Unit` | 无参无返回值 |
| `type CB = (x: T) => void` | `public type CB = (x: T) -> Unit` | 单参数无返回值 |
| `type CB = (x: T1, y: T2) => void` | `public type CB = (x: T1, y: T2) -> Unit` | 多参数无返回值 |
| `type CB = (x: T) => R` | `public type CB = (x: T) -> R` | 带返回值 |
| `type CB = (x: T) => [R1, R2]` | `public type CB = (x: T) -> (R1, R2)` | 返回元组 |
| `callback?: CB` | `callback: Option<CB>` | 可选回调属性 |
| `callback: CB \| undefined` | `callback: Option<CB>` | 联合 undefined |
| `(x: T) => R` | `((x: T) -> R)` | 内联回调类型 |
| `void` 返回类型 | `Unit` | void 对应 Unit |
| `=>` 箭头 | `->` | 函数类型箭头 |
---
#### 1.8.2 回调与序列化(仅标记,细节见《ArkTS-Cangjie 序列化规则》)
**核心序列化方法**:
回调函数使用 `SerializerBase.holdAndWriteCallback()` 方法进行序列化:
```cangjie
// 序列化回调函数
thisSerializer.holdAndWriteCallback(callbackValue)
```
**序列化流程**:
1. **检查可选性**:使用 `if-let` 模式匹配 `Option<CallbackType>`
2. **写入类型标记**:
- 有回调:`RuntimeType.OBJECT.ordinal`
- 无回调:`RuntimeType.UNDEFINED.ordinal`
3. **序列化回调引用**:调用 `holdAndWriteCallback(callback)`
4. **调用 Native 方法**:传递序列化后的字节数组
5. **释放序列化器**:调用 `thisSerializer.release()`
**示例**:
```cangjie
public func onClickAttribute(event: Option<((event: ClickEvent) -> Unit)>): Unit {
// 1. 获取序列化器
let thisSerializer: SerializerBase = SerializerBase.hold()
// 2. 检查可选性并序列化
if (let Some(event) <- event) {
// 有回调:写入 OBJECT 标记
thisSerializer.writeInt8(RuntimeType.OBJECT.ordinal)
let eventTmpValue = event
// 序列化回调引用
thisSerializer.holdAndWriteCallback(eventTmpValue)
} else {
// 无回调:写入 UNDEFINED 标记
thisSerializer.writeInt8(RuntimeType.UNDEFINED.ordinal)
}
// 3. 调用 Native 方法
ArkUIGeneratedNativeModule._CommonMethod_setOnClick0(
this.peer.ptr,
thisSerializer.asBuffer(),
thisSerializer.length()
)
// 4. 释放序列化器
thisSerializer.release()
}
```
**关键点**:
1. **回调函数不直接序列化函数体**:而是序列化回调的引用(句柄)
2. **`holdAndWriteCallback` 的作用**:
- 在 Cangjie 侧持有回调引用
- 将回调句柄写入字节流
- 确保回调在 Native 侧可以被调用
3. **生命周期管理**:回调引用由运行时管理,避免过早释放
**属性中的回调序列化**:
```cangjie
// ArkGridLayoutOptions_serializer.cj
public class GridLayoutOptions_serializer {
public static func write(buffer: SerializerBase, value: GridLayoutOptions): Unit {
var valueSerializer: SerializerBase = buffer
// ... 其他属性序列化 ...
// 回调函数属性序列化
let valueHolderForOnGetIrregularSizeByIndex = value.onGetIrregularSizeByIndex
if (let Some(valueHolderForOnGetIrregularSizeByIndex) <- valueHolderForOnGetIrregularSizeByIndex) {
valueSerializer.writeInt8(RuntimeType.OBJECT.ordinal)
let valueHolderForOnGetIrregularSizeByIndexTmpValue = valueHolderForOnGetIrregularSizeByIndex
// 使用 holdAndWriteCallback 序列化回调函数
valueSerializer.holdAndWriteCallback(valueHolderForOnGetIrregularSizeByIndexTmpValue)
} else {
valueSerializer.writeInt8(RuntimeType.UNDEFINED.ordinal)
}
let valueHolderForOnGetRectByIndex = value.onGetRectByIndex
if (let Some(valueHolderForOnGetRectByIndex) <- valueHolderForOnGetRectByIndex) {
valueSerializer.writeInt8(RuntimeType.OBJECT.ordinal)
let valueHolderForOnGetRectByIndexTmpValue = valueHolderForOnGetRectByIndex
valueSerializer.holdAndWriteCallback(valueHolderForOnGetRectByIndexTmpValue)
} else {
valueSerializer.writeInt8(RuntimeType.UNDEFINED.ordinal)
}
}
}
```
详细的序列化字节格式和反序列化流程请参见《ArkTS-Cangjie 序列化规则》文档。
---
#### 1.8.3 泛型回调翻译
说明:
泛型回调函数在当前测试集中较少使用。根据项目的泛型处理策略(详见 1.5 泛型章节),泛型回调可能需要:
1. **使用 `This` 类型**:对于组件链式调用中的泛型
2. **类型具体化**:将泛型参数替换为具体类型
3. **接口模拟**:使用接口约束代替泛型约束
**示例**(假设场景):
```typescript
// ArkTS - 泛型回调
type GenericCallback<T> = (value: T) => void;
interface DataSource<T> {
onDataChange?: GenericCallback<T>;
}
```
**可能的翻译方式**:
```cangjie
// 方式1:类型具体化(当前项目采用)
public type DataChangeCallback = (value: Any) -> Unit
public class DataSource {
public var onDataChange: Option<DataChangeCallback>
}
// 方式2:如果 Cangjie 支持泛型(语言支持,但项目选择不用)
// public type GenericCallback<T> = (value: T) -> Unit
// public class DataSource<T> {
// public var onDataChange: Option<GenericCallback<T>>
// }
```
注意:当前项目选择不使用 Cangjie 的泛型特性,而是通过类型具体化和接口约束来处理。
---
#### 1.8.4 回调函数类型总结
**核心转换规则**:
| 特性 | ArkTS | Cangjie |
|------|-------|---------|
| **类型别名** | `type CB = (x: T) => R` | `public type CB = (x: T) -> R` |
| **函数箭头** | `=>` | `->` |
| **返回 void** | `=> void` | `-> Unit` |
| **返回元组** | `=> [T1, T2]` | `-> (T1, T2)` |
| **可选回调** | `callback?: CB` 或 `CB \| undefined` | `callback: Option<CB>` |
| **内联类型** | `(x: T) => R` | `((x: T) -> R)` |
| **序列化** | N/A | `holdAndWriteCallback(callback)` |
**关键发现**:
1. **类型别名优先**:
- Component 层倾向使用类型别名(如 `VoidCallback`、`OnTextCopyCallback`)
- Peer 层使用内联函数类型(如 `((value: String) -> Unit)`)
- 提高代码可读性和类型复用
2. **统一的可选性处理**:
- `callback?: Type` 和 `callback: Type | undefined` 都转换为 `Option<Type>`
- 无论是类型别名还是内联类型
3. **专用序列化方法**:
- 回调函数使用 `holdAndWriteCallback` 而非普通的 `write` 方法
- 序列化的是回调引用,而非函数体
- 涉及跨语言调用的生命周期管理
4. **返回值类型映射**:
- `void` → `Unit`
- 元组 `[T1, T2]` → `(T1, T2)`
- 其他类型遵循基本类型映射规则
5. **参数名保留**:
- 回调函数的参数名在转换时保留
- 有助于代码可读性和 IDE 提示
**设计考虑**:
1. **为什么 Component 层使用类型别名?**
- 提高代码可读性(`VoidCallback` 比 `(() -> Unit)` 更清晰)
- 便于类型复用和维护
- 更好的 IDE 支持
2. **为什么 Peer 层使用内联类型?**
- 直接反映 ArkTS 的内联回调类型
- 减少类型别名的依赖
- 序列化逻辑更直观
3. **为什么需要 `holdAndWriteCallback`?**
- 回调函数涉及跨语言调用(Cangjie ↔ Native)
- 需要保持回调引用的生命周期
- 不能简单地序列化函数体(函数是闭包,包含上下文)
4. **回调函数与普通函数的区别**:
- 回调函数是作为参数传递的函数引用
- 需要跨语言边界调用
- 生命周期由调用方管理
### 1.9 类型别名(Type Alias)
类型别名使用 `type` 关键字为现有类型定义新名称,提高代码可读性和类型复用。
#### 1.9.1 简单类型别名
简单类型别名为基础类型或单一类型定义别名。
测试集中的用法:
```typescript
// units.d.ts - 字符串类型别名
type PX = string; // 像素单位
type FP = string; // 字体像素单位
type LPX = string; // 逻辑像素单位
type Percentage = string; // 百分比
type Degree = string; // 角度单位
// units.d.ts - 接口类型别名
type Margin = Padding;
type EdgeWidth = EdgeWidths;
type LocalizedMargin = LocalizedPadding;
```
转换结果:
简单类型别名在 Cangjie 中**不生成独立代码**,使用时直接展开为原始类型:
```typescript
// ArkTS 定义
type PX = string;
property: PX;
// Cangjie 翻译时展开
// property: string → property: String
```
转换规则:
- 简单类型别名不生成 Cangjie 类型定义
- 使用时展开为原始类型,再按照相应的类型映射规则转换
- `type PX = string` → 使用时展开为 `String`
- `type Margin = Padding` → 使用时展开为 `Padding` 类
说明:简单类型别名主要用于提高代码可读性,在代码生成时会被展开为实际类型。
#### 1.9.2 联合类型别名
联合类型别名为联合类型定义名称,是最常用的类型别名形式。
测试集中的用法:
```typescript
// units.d.ts - 联合类型别名
type Length = string | double | Resource;
type VP = string | double;
type Dimension = PX | VP | FP | LPX | Percentage | Resource;
type ResourceStr = string | Resource;
type ResourceColor = Color | number | string | Resource;
// enums.d.ts - 泛型联合类型别名
type Nullable<T> = T | undefined;
```
转换结果:
联合类型别名转换为 Cangjie 的 **Selector 类**(详见 1.7 节):
```cangjie
// Length 联合类型生成 Selector 类
public class Length {
private var selector: Int32
public func getSelector(): Int32 {
return selector
}
// 分支 0: string
private var value0: Option<String> = None<String>
public init(param: String) {
value0 = param
selector = 0
}
public func getValue0(): String { ... }
// 分支 1: Float64 (double)
private var value1: Option<Float64> = None<Float64>
public init(param: Float64) {
value1 = param
selector = 1
}
public func getValue1(): Float64 { ... }
// 分支 2: Resource
private var value2: Option<Resource> = None<Resource>
public init(param: Resource) {
value2 = param
selector = 2
}
public func getValue2(): Resource { ... }
}
// ResourceStr 联合类型生成 Selector 类
public class ResourceStr {
private var selector: Int32
public func getSelector(): Int32 { return selector }
// 分支 0: string
private var value0: Option<String> = None<String>
public init(param: String) { value0 = param; selector = 0 }
public func getValue0(): String { ... }
// 分支 1: Resource
private var value1: Option<Resource> = None<Resource>
public init(param: Resource) { value1 = param; selector = 1 }
public func getValue1(): Resource { ... }
}
```
转换规则:
- `type T = A | B | C` → 生成 Selector 类 `class T { selector, valueN, ... }`
- 每个联合分支生成一个构造器和对应的 valueN 字段
- 泛型联合类型别名(如 `Nullable<T>`)不生成独立定义,使用时展开
说明:联合类型别名是唯一会生成独立 Cangjie 类型定义的类型别名。详细的 Selector 模式说明见 1.7 节"联合类型"。
#### 1.9.3 函数类型别名
函数类型别名为回调函数类型定义名称。
测试集中的用法:
```typescript
// units.d.ts - 无参回调
type VoidCallback = () => void;
// button.d.ts - 带参数回调
type ButtonTriggerClickCallback = (xPos: double, yPos: double) => void;
// text_common.d.ts - 带参数和返回值
type OnCreateMenuCallback = (menuItems: Array<TextMenuItem>) => Array<TextMenuItem>;
type OnMenuItemClickCallback = (menuItem: TextMenuItem, range: TextRange) => boolean;
// scroll.d.ts - 复杂回调
type ScrollOnScrollCallback = (xOffset: double, yOffset: double, scrollState: ScrollState) => void;
// styled_string.d.ts - 带 ArrayBuffer 的回调
type StyledStringMarshallCallback = (marshallableVal: StyledStringMarshallingValue) => ArrayBuffer;
```
转换结果:
函数类型别名转换为 Cangjie 的 **类型别名(type)**:
```cangjie
// Common.cj
// 无参回调
public type VoidCallback = () -> Unit
// 带参数回调
public type ButtonTriggerClickCallback = (xPos: Float64, yPos: Float64) -> Unit
// 带参数和返回值
public type OnCreateMenuCallback = (menuItems: ArrayList<TextMenuItem>) -> ArrayList<TextMenuItem>
public type OnMenuItemClickCallback = (menuItem: TextMenuItem, range: TextRange) -> Bool
// 复杂回调
public type ScrollOnScrollCallback = (
xOffset: Float64,
yOffset: Float64,
scrollState: ScrollState
) -> Unit
// 带 ArrayBuffer 的回调
public type StyledStringMarshallCallback = (
marshallableVal: StyledStringMarshallingValue
) -> ArrayBuffer
```
转换规则:
- `type CB = (params) => ReturnType` → `public type CB = (params) -> ReturnType`
- `=>` 箭头 → `->` 箭头
- `void` 返回类型 → `Unit`
- 参数类型和返回类型遵循基础类型映射规则
说明:函数类型别名会生成 Cangjie 的 `public type` 定义。详细的回调函数翻译规则见 1.8 节"回调函数类型"。
#### 1.9.4 外部模块类型引用(type Xxx = _Xxx)
外部模块类型引用用于引用其他模块的类型,通常使用下划线前缀表示外部类型。
测试集中的用法:
```typescript
// units.d.ts - 引用外部模块类型
type LengthMetricsUnit = _LengthMetricsUnit;
// type LengthMetrics = _LengthMetrics; // 注释掉的
// type ColorMetrics = _ColorMetrics; // 注释掉的
// units.d.ts - 引用 drawing 模块
type DrawingCanvas = _drawing.Canvas;
// enums.d.ts - 引用 curves 模块
type Curve = curves.Curve;
// image.d.ts - 引用其他模块
type DrawingColorFilter = drawing.ColorFilter;
type ResolutionQuality = image.ResolutionQuality;
type DrawingLattice = drawing.Lattice;
type ImageMatrix = matrix4.Matrix4Transit;
```
转换结果:
外部模块类型引用在 Cangjie 中**不生成代码**或转换为 `Any` 类型:
```cangjie
// 方式1:不生成代码(最常见)
// type LengthMetricsUnit = _LengthMetricsUnit;
// → 不生成任何 Cangjie 代码
// 方式2:转换为 Any(如果被使用)
// type Curve = curves.Curve;
// → public type Curve = Any
// 方式3:转换为具体类型(如果有对应实现)
// type DrawingCanvas = _drawing.Canvas;
// → public type DrawingCanvas = Canvas(如果 Canvas 类已定义)
```
转换规则:
- 外部模块类型引用通常不生成 Cangjie 代码
- 如果外部类型在当前项目中有对应实现,则使用该实现
- 如果外部类型被实际使用但无对应实现,则转换为 `Any`
- 注释掉的类型别名不处理
说明:
1. 外部模块类型主要用于类型声明的完整性,但在代码生成时通常不需要
2. 当前项目专注于 UI 组件的类型转换,外部模块类型(如 drawing、curves 等)不在转换范围内
3. 如果外部类型被实际使用,需要根据具体情况决定是否生成对应的 Cangjie 类型
#### 1.9.5 类型别名处理总结
测试集统计:
- 简单类型别名:约 10 个(`PX`、`Margin` 等)
- 联合类型别名:约 10 个(`Length`、`ResourceColor` 等)
- 函数类型别名:约 20 个(`VoidCallback`、`ButtonTriggerClickCallback` 等)
- 外部模块类型引用:约 10 个(`Curve`、`DrawingCanvas` 等)
转换策略总结:
| 类型别名类型 | ArkTS 语法 | Cangjie 转换 | 状态 |
|------------|-----------|-------------|------|
| 简单类型别名 | `type T = string` | 使用时展开 | 不生成代码 |
| 联合类型别名 | `type T = A \| B` | `class T { selector, ... }` | 生成 Selector 类 |
| 函数类型别名 | `type CB = () => void` | `public type CB = () -> Unit` | 生成 type 定义 |
| 外部模块引用 | `type T = _T` | 不生成或 `Any` | 按需处理 |
注意事项:
1. **只有联合类型别名和函数类型别名会生成 Cangjie 代码**
2. 简单类型别名在使用时展开为原始类型
3. 外部模块类型引用通常不生成代码
4. 泛型类型别名(如 `Nullable<T>`)不生成独立定义,使用时展开
5. 类型别名的命名在 Cangjie 中保持不变(PascalCase)
关联章节:
- 联合类型别名的详细处理见 1.7 节"联合类型"
- 函数类型别名的详细处理见 1.8 节"回调函数类型"
- 泛型类型别名的处理见 1.5 节"泛型"
---