# Go vs TypeScript + class-transformer 对比

## 命名约定和自动转换实现

本文档展示 Go 和 TypeScript 如何实现类似的自动类型转换机制。

---

## 📋 命名约定总结

### OpenAPI 规范

| 元素 | 命名约定 | 示例 |
|------|---------|------|
| Fixed Fields | `camelCase` | `operationId`, `requestBody` |
| Schema 名称 | `PascalCase` | `ClusterResponse`, `CreateClusterRequest` |
| Schema 属性 | `snake_case` | `has_ca_data`, `created_at`, `cluster_id` |

### Backend Go

| 元素 | 命名约定 | 示例 |
|------|---------|------|
| 导出变量/字段 | `PascalCase` | `HasCAData`, `CreatedAt` |
| 非导出变量 | `camelCase` | `hasCAData`, `createdAt` |
| 类型名 | `PascalCase` | `ClusterResponse`, `CreateClusterRequest` |
| JSON 标签 | `snake_case` | `json:"has_ca_data"`, `json:"created_at"` |

### Frontend TypeScript

| 元素 | 命名约定 | 示例 |
|------|---------|------|
| 变量 | `camelCase` | `hasCAData`, `createdAt` |
| 类型名 | `PascalCase` | `Cluster`, `CreateClusterRequest` |
| JSON | `snake_case` | `has_ca_data`, `created_at` |

---

## 🔄 自动转换对比

### Go 的实现

```go
// backend/internal/adapter/input/http/dto/cluster_dto.go

package dto

// 类型定义：PascalCase
type ClusterResponse struct {
    // 导出字段：PascalCase
    ID          string `json:"id"`
    Name        string `json:"name"`
    Host        string `json:"host"`
    
    // 字段名：PascalCase, JSON：snake_case
    HasCAData   bool   `json:"has_ca_data"`    // ← struct tag
    HasCertData bool   `json:"has_cert_data"`  // ← struct tag
    HasKeyData  bool   `json:"has_key_data"`   // ← struct tag
    
    CAData      string `json:"ca_data"`        // ← struct tag
    CertData    string `json:"cert_data"`      // ← struct tag
    KeyData     string `json:"key_data"`       // ← struct tag
    
    CreatedAt   string `json:"created_at"`     // ← struct tag
    UpdatedAt   string `json:"updated_at"`     // ← struct tag
}

// 使用 - Go 自动转换
func GetCluster() ClusterResponse {
    cluster := ClusterResponse{
        ID:          "cluster-123",
        Name:        "Production",
        HasCAData:   true,          // 内部使用 PascalCase
        CAData:      "••••••••",
        CreatedAt:   "2025-11-10",
    }
    
    // json.Marshal 自动转换为 snake_case
    // {"id":"cluster-123","has_ca_data":true,"ca_data":"••••••••","created_at":"2025-11-10"}
    return cluster
}
```

### TypeScript + class-transformer 的实现

```typescript
// frontend/src/api/models/cluster.model.ts

import { Expose } from 'class-transformer';

// 类型定义：PascalCase
export class Cluster {
  @Expose()
  id!: string;
  
  @Expose()
  name!: string;
  
  @Expose()
  host!: string;
  
  // 字段名：camelCase, JSON：snake_case
  @Expose({ name: 'has_ca_data' })    // ← @Expose decorator (类似 struct tag)
  hasCAData?: boolean;
  
  @Expose({ name: 'has_cert_data' })  // ← @Expose decorator
  hasCertData?: boolean;
  
  @Expose({ name: 'has_key_data' })   // ← @Expose decorator
  hasKeyData?: boolean;
  
  @Expose({ name: 'ca_data' })        // ← @Expose decorator
  caData?: string;
  
  @Expose({ name: 'cert_data' })      // ← @Expose decorator
  certData?: string;
  
  @Expose({ name: 'key_data' })       // ← @Expose decorator
  keyData?: string;
  
  @Expose({ name: 'created_at' })     // ← @Expose decorator
  createdAt!: string;
  
  @Expose({ name: 'updated_at' })     // ← @Expose decorator
  updatedAt!: string;
}

// 使用 - TypeScript + class-transformer 自动转换
import { fromJson, toJson } from '@/api/serializer';

function getCluster(): Cluster {
  // JSON → 类实例 (snake_case → camelCase)
  const apiResponse = {
    id: "cluster-123",
    name: "Production",
    has_ca_data: true,      // JSON: snake_case
    ca_data: "••••••••",
    created_at: "2025-11-10",
  };
  
  const cluster = fromJson(Cluster, apiResponse);
  
  // 内部使用 camelCase
  console.log(cluster.hasCAData);   // true
  console.log(cluster.caData);       // "••••••••"
  console.log(cluster.createdAt);    // "2025-11-10"
  
  return cluster;
}
```

---

## 🔍 详细对比

### 1. 结构定义

#### Go
```go
type ClusterResponse struct {
    HasCAData   bool   `json:"has_ca_data"`
    CreatedAt   string `json:"created_at"`
}
```

#### TypeScript + class-transformer
```typescript
class Cluster {
  @Expose({ name: 'has_ca_data' })
  hasCAData?: boolean;
  
  @Expose({ name: 'created_at' })
  createdAt!: string;
}
```

**对应关系**:
- Go 的 `struct tag` ↔ TypeScript 的 `@Expose` 装饰器
- Go 的 `json:"field_name"` ↔ TypeScript 的 `{ name: 'field_name' }`

---

### 2. JSON 序列化（结构体/类 → JSON）

#### Go
```go
cluster := ClusterResponse{
    HasCAData: true,        // PascalCase
    CreatedAt: "2025-11-10",
}

jsonBytes, _ := json.Marshal(cluster)
// 自动转换为: {"has_ca_data":true,"created_at":"2025-11-10"}
```

#### TypeScript + class-transformer
```typescript
const cluster = new Cluster();
cluster.hasCAData = true;      // camelCase
cluster.createdAt = "2025-11-10";

const json = toJson(cluster);
// 自动转换为: {has_ca_data: true, created_at: "2025-11-10"}
```

**对应关系**:
- Go 的 `json.Marshal()` ↔ TypeScript 的 `toJson()`
- 都实现了：内部字段名 → JSON snake_case

---

### 3. JSON 反序列化（JSON → 结构体/类）

#### Go
```go
jsonStr := `{"has_ca_data":true,"created_at":"2025-11-10"}`
var cluster ClusterResponse
json.Unmarshal([]byte(jsonStr), &cluster)

// 自动映射到 PascalCase 字段
fmt.Println(cluster.HasCAData)   // true
fmt.Println(cluster.CreatedAt)   // "2025-11-10"
```

#### TypeScript + class-transformer
```typescript
const apiResponse = {
  has_ca_data: true,
  created_at: "2025-11-10"
};

const cluster = fromJson(Cluster, apiResponse);

// 自动映射到 camelCase 字段
console.log(cluster.hasCAData);   // true
console.log(cluster.createdAt);   // "2025-11-10"
```

**对应关系**:
- Go 的 `json.Unmarshal()` ↔ TypeScript 的 `fromJson()`
- 都实现了：JSON snake_case → 内部字段名

---

## 📊 完整数据流转示例

### Scenario: 创建集群

```
┌─────────────────────────────────────────────────────────────────┐
│ 1. Frontend 组件 (TypeScript camelCase)                          │
├─────────────────────────────────────────────────────────────────┤
│ const request = new CreateClusterRequest();                     │
│ request.name = "Production";                                    │
│ request.caData = "LS0t...";      // camelCase                   │
│ request.certData = "LS0t...";                                   │
└─────────────────────────────────────────────────────────────────┘
                    ↓ toJson(request)
┌─────────────────────────────────────────────────────────────────┐
│ 2. HTTP Request Body (JSON snake_case)                          │
├─────────────────────────────────────────────────────────────────┤
│ {                                                                │
│   "name": "Production",                                          │
│   "ca_data": "LS0t...",          // snake_case                  │
│   "cert_data": "LS0t..."                                         │
│ }                                                                │
└─────────────────────────────────────────────────────────────────┘
                    ↓ HTTP POST
┌─────────────────────────────────────────────────────────────────┐
│ 3. Backend Go (PascalCase struct)                               │
├─────────────────────────────────────────────────────────────────┤
│ type CreateClusterRequest struct {                              │
│   Name     string `json:"name"`                                 │
│   CAData   string `json:"ca_data"`     // PascalCase            │
│   CertData string `json:"cert_data"`                            │
│ }                                                                │
│                                                                  │
│ // json.Unmarshal 自动映射                                        │
│ var req CreateClusterRequest                                    │
│ json.Unmarshal(body, &req)                                      │
│ // req.CAData = "LS0t..."        // 自动转换！                    │
└─────────────────────────────────────────────────────────────────┘
                    ↓ 处理业务逻辑
┌─────────────────────────────────────────────────────────────────┐
│ 4. Backend Response (JSON snake_case)                           │
├─────────────────────────────────────────────────────────────────┤
│ {                                                                │
│   "id": "cluster-123",                                           │
│   "name": "Production",                                          │
│   "has_ca_data": true,           // snake_case                  │
│   "created_at": "2025-11-10T08:00:00Z"                          │
│ }                                                                │
└─────────────────────────────────────────────────────────────────┘
                    ↓ fromJson(Cluster, response)
┌─────────────────────────────────────────────────────────────────┐
│ 5. Frontend 使用 (TypeScript camelCase)                          │
├─────────────────────────────────────────────────────────────────┤
│ const cluster: Cluster = await createCluster(request);          │
│                                                                  │
│ // 使用 camelCase                                                │
│ console.log(cluster.hasCAData);   // true                       │
│ console.log(cluster.createdAt);   // "2025-11-10T08:00:00Z"    │
│                                                                  │
│ // 在 React 组件中                                               │
│ {cluster.hasCAData && <Badge>Has CA</Badge>}                   │
└─────────────────────────────────────────────────────────────────┘
```

---

## 🎯 关键相似点

| 特性 | Go | TypeScript + class-transformer |
|------|----|---------------------------------|
| **元数据标记** | `struct tags` | `@Expose` 装饰器 |
| **内部命名** | `PascalCase` | `camelCase` |
| **JSON 命名** | `snake_case` | `snake_case` |
| **序列化** | `json.Marshal()` | `toJson()` |
| **反序列化** | `json.Unmarshal()` | `fromJson()` |
| **自动转换** | ✅ 内置支持 | ✅ 通过 class-transformer |

---

## 🔧 实现代码对比

### Go - 完整示例

```go
package dto

import "encoding/json"

type ClusterResponse struct {
    ID          string `json:"id"`
    Name        string `json:"name"`
    HasCAData   bool   `json:"has_ca_data"`
    CreatedAt   string `json:"created_at"`
}

func main() {
    // 创建实例
    cluster := ClusterResponse{
        ID:        "123",
        Name:      "Test",
        HasCAData: true,
        CreatedAt: "2025-11-10",
    }
    
    // 序列化
    jsonBytes, _ := json.Marshal(cluster)
    // Output: {"id":"123","name":"Test","has_ca_data":true,"created_at":"2025-11-10"}
    
    // 反序列化
    var newCluster ClusterResponse
    json.Unmarshal(jsonBytes, &newCluster)
    // newCluster.HasCAData = true
}
```

### TypeScript - 完整示例

```typescript
import { Expose } from 'class-transformer';
import { fromJson, toJson } from '@/api/serializer';

class Cluster {
  @Expose() id!: string;
  @Expose() name!: string;
  @Expose({ name: 'has_ca_data' }) hasCAData?: boolean;
  @Expose({ name: 'created_at' }) createdAt!: string;
}

function main() {
  // 创建实例
  const cluster = new Cluster();
  cluster.id = "123";
  cluster.name = "Test";
  cluster.hasCAData = true;
  cluster.createdAt = "2025-11-10";
  
  // 序列化
  const json = toJson(cluster);
  // Output: {id:"123",name:"Test",has_ca_data:true,created_at:"2025-11-10"}
  
  // 反序列化
  const newCluster = fromJson(Cluster, json);
  // newCluster.hasCAData === true
}
```

---

## 📝 OpenAPI 驱动开发

### OpenAPI 规范 → Go

```yaml
# backend/docs/openapi.yaml

components:
  schemas:
    ClusterResponse:              # → type ClusterResponse struct
      properties:
        id:                        # → ID string `json:"id"`
          type: string
        has_ca_data:              # → HasCAData bool `json:"has_ca_data"`
          type: boolean
        created_at:               # → CreatedAt string `json:"created_at"`
          type: string
```

### OpenAPI 规范 → TypeScript

```yaml
# backend/docs/openapi.yaml

components:
  schemas:
    ClusterResponse:              # → class Cluster
      properties:
        id:                        # → @Expose() id!: string
          type: string
        has_ca_data:              # → @Expose({ name: 'has_ca_data' }) hasCAData?: boolean
          type: boolean
        created_at:               # → @Expose({ name: 'created_at' }) createdAt!: string
          type: string
```

---

## ✨ 总结

### Go 的优势
- ✅ 内置支持，无需额外库
- ✅ 编译时生成代码
- ✅ 零运行时开销

### TypeScript + class-transformer 的优势
- ✅ 与 Go 相似的开发体验
- ✅ 装饰器语法清晰
- ✅ 类型安全
- ✅ 运行时开销极小

### 共同点
- ✅ 都使用元数据标记字段映射
- ✅ 都实现了自动类型转换
- ✅ 都保持了代码的可读性和可维护性
- ✅ 都支持 OpenAPI 驱动开发

---

**结论**: TypeScript + class-transformer 成功复现了 Go 的 struct tags 机制，为前端开发提供了同样优雅的类型转换体验！

---

**创建日期**: 2025-11-10  
**作者**: AI Assistant


