* 方案 1: 对象字面量类型(推荐)⭐⭐⭐⭐⭐
* 优点:不需要额外接口,类型安全
* 缺点:字段多时参数类型较长(但生成代码可接受)
*/
export class UserInfo1 extends Message {
uid: bigint = 0n
name: string = ''
realm: string = ''
static create(init?: {
uid?: bigint
name?: string
realm?: string
}): UserInfo1 {
const msg = new UserInfo1()
if (init) {
if (init.uid !== undefined) msg.uid = init.uid
if (init.name !== undefined) msg.name = init.name
if (init.realm !== undefined) msg.realm = init.realm
}
return msg
}
}
* 方案 2: Record<string, unknown>(次选)⭐⭐⭐
* 优点:简洁
* 缺点:失去类型安全
*/
export class UserInfo2 extends Message {
uid: bigint = 0n
name: string = ''
static create(init?: Record<string, unknown>): UserInfo2 {
const msg = new UserInfo2()
if (init) {
if ('uid' in init && init.uid !== undefined) {
msg.uid = init.uid as bigint
}
if ('name' in init && init.name !== undefined) {
msg.name = init.name as string
}
}
return msg
}
}
* 方案 3: any(不推荐)⭐
* 优点:简单
* 缺点:完全失去类型安全,ArkTS 可能也不支持 any
*/
export class UserInfo3 extends Message {
uid: bigint = 0n
name: string = ''
static create(init?: any): UserInfo3 {
const msg = new UserInfo3()
if (init) {
if (init.uid !== undefined) msg.uid = init.uid
if (init.name !== undefined) msg.name = init.name
}
return msg
}
}
* 方案 4: Object(不推荐)⭐
* 优点:ArkTS 支持
* 缺点:完全失去类型安全
*/
export class UserInfo4 extends Message {
uid: bigint = 0n
name: string = ''
static create(init?: Object): UserInfo4 {
const msg = new UserInfo4()
if (init) {
const obj = init as any
if ('uid' in obj) msg.uid = obj.uid
if ('name' in obj) msg.name = obj.name
}
return msg
}
}
* 方案 5: 类型别名(与 *Init 接口本质相同)⭐⭐⭐⭐
* 优点:类型安全,可以用 type 代替 interface
* 缺点:还是需要定义额外的类型
*/
type UserInfo5Init = {
uid?: bigint
name?: string
realm?: string
}
export class UserInfo5 extends Message {
uid: bigint = 0n
name: string = ''
realm: string = ''
static create(init?: UserInfo5Init): UserInfo5 {
const msg = new UserInfo5()
if (init) {
if (init.uid !== undefined) msg.uid = init.uid
if (init.name !== undefined) msg.name = init.name
if (init.realm !== undefined) msg.realm = init.realm
}
return msg
}
}
* 使用示例对比
*/
function testUsage() {
const user1 = UserInfo1.create({
uid: 123n,
name: 'Alice'
})
const user2 = UserInfo2.create({
uid: 123n,
name: 'Alice',
nonExistent: 'allowed'
})
const user5 = UserInfo5.create({
uid: 123n,
name: 'Alice'
})
}
* 方案对比表
*
* | 方案 | 类型安全 | 需要额外定义 | ArkTS兼容 | IntelliSense | 推荐度 |
* |-----|---------|-------------|-----------|-------------|--------|
* | 1. 对象字面量 | ✅ 完全 | ❌ 不需要 | ✅ 支持 | ✅ 完美 | ⭐⭐⭐⭐⭐ |
* | 2. Record | ❌ 无 | ❌ 不需要 | ✅ 支持 | ❌ 无 | ⭐⭐ |
* | 3. any | ❌ 无 | ❌ 不需要 | ⚠️ 不推荐 | ❌ 无 | ⭐ |
* | 4. Object | ❌ 无 | ❌ 不需要 | ✅ 支持 | ❌ 无 | ⭐ |
* | 5. 类型别名 | ✅ 完全 | ✅ 需要 | ✅ 支持 | ✅ 完美 | ⭐⭐⭐⭐ |
*/
* 推荐理由:
*
* 1. ✅ 完全类型安全
* - 字段名称检查
* - 字段类型检查
* - 编译时错误提示
*
* 2. ✅ 不需要额外的类型定义
* - 不需要生成 *Init 接口
* - 不需要生成 type 别名
* - 代码更简洁
*
* 3. ✅ ArkTS 完全支持
* - 对象字面量类型是 TypeScript/ArkTS 的基础特性
* - 不使用任何高级类型特性
* - 不需要 Partial<T> 等工具类型
*
* 4. ✅ 优秀的开发体验
* - IDE 自动补全
* - 类型提示
* - 重构支持
*
* 5. ✅ 生成代码可接受
* - 虽然参数类型较长
* - 但是生成代码,不影响可读性
* - 用户调用时看到的是类型提示,不是源代码
*
* 唯一缺点:
* - ⚠️ 字段多时参数类型会很长
* 但这不是问题,因为:
* - 这是生成代码,不是手写代码
* - 用户看到的是 IDE 提示,不是源码
* - 类型信息完整,反而更好
*/
* 在 arkpb-gen.js 中的实现:
*
* ```javascript
* // 生成 create() 方法
* function generateCreateMethod(className, fields) {
* // 1. 生成参数类型(对象字面量)
* const paramType = fields.length > 0
* ? `{\n ${fields.map(f => `${f.name}?: ${fieldType(f)}`).join('\n ')}\n }`
* : '{}'
*
* // 2. 生成方法签名
* lines.push(` static create(init?: ${paramType}): ${className} {`)
* lines.push(` const msg = new ${className}()`)
*
* // 3. 生成字段赋值
* lines.push(` if (init) {`)
* for (const f of fields) {
* lines.push(` if (init.${f.name} !== undefined) msg.${f.name} = init.${f.name}`)
* }
* lines.push(` }`)
* lines.push(` return msg`)
* lines.push(` }`)
* }
* ```
*/
* 最终决定:使用方案 1 - 对象字面量类型
*
* 理由:
* 1. 符合用户要求:不需要生成额外的 *Init 接口
* 2. 类型安全:完全保留类型检查
* 3. ArkTS 兼容:使用基础语法,无兼容性问题
* 4. 开发体验好:完整的 IDE 支持
* 5. 实现简单:修改生成器即可
*
* 下一步:
* 1. 修改 arkpb-gen.js 中的 create() 方法生成逻辑
* 2. 使用对象字面量类型替代 Partial<T>
* 3. 重新生成代码
* 4. 测试编译
*/
export const RECOMMENDATION = {
solution: 'Object Literal Types',
rating: 5,
needsExtraInterface: false,
typeSafe: true,
arktsCompatible: true,
reason: '完全类型安全,不需要额外接口,ArkTS完全支持'
}