Skip to content

结构体与组合

Targo 支持 class,但语义是 Go-first。把它理解为"带方法和引用语义的 struct",而不是"完整的 JavaScript class 运行时"。

核心规则

  • class 映射到 Go struct,引用语义(Go 中为 *T)。
  • constructor 支持——编译为 NewT() 工厂函数。
  • 不支持经典继承。
  • 优先使用组合、接口、Embedding<{ ... }>()DefinedType<...>(),而不是试图重现 extends
  • 私有字段使用 #field。不要使用 TypeScript 的 privateprotected

字段可见性与大小写

Go 使用首字母大小写控制可见性。Targo 自动映射:

Targo 语法Go 输出可见性
name: stringName string公开(默认——首字母大写)
#secret: stringsecret string私有(包内部)
_internal: int_Internal int公开(_ 保留,下一个字母大写)
typescript
class User {
  name: string = "";       // → Name string(公开)
  #password: string = "";  // → password string(私有)
}

#field 只能在类内部访问。外部访问需要 getter/setter 方法。

构造函数

当类有 constructor 时,new T(...) 编译为 NewT(...)

typescript
class User {
  Name: string;
  constructor(name: string) {
    this.Name = name;
  }
}

let u = new User("Alice");  // Go: NewUser("Alice")

当类没有 constructor 时,new T() 编译为 new(T)(Go 零值分配):

typescript
class Point { X: float64 = 0; Y: float64 = 0; }
let p = new Point();  // Go: new(Point)

Struct 字面量初始化绕过构造函数:

typescript
let u: User = { Name: "Bob" };  // Go: &User{Name: "Bob"}

构造函数约束:

  • 只能有一个构造函数(Go 没有函数重载)
  • 没有 super()(没有继承)
  • DefinedType 类不能有构造函数——使用 as 或工厂函数代替

方法与接收者

方法默认使用指针接收者(匹配约 83% 的 Go 标准库方法):

typescript
class Counter {
  Value: int = 0;

  Increment(): void {       // Go: func (c *Counter) Increment()
    this.Value++;
  }
}

需要值接收者时,使用显式 this: Val<T>

typescript
class Point {
  X: float64 = 0;
  Y: float64 = 0;

  Distance(this: Val<Point>): float64 {  // Go: func (p Point) Distance() float64
    return Math.sqrt(this.X * this.X + this.Y * this.Y);
  }
}

方法可见性遵循与字段相同的大小写规则——所有方法在 Go 输出中都是公开的(首字母大写)。

可选字段

使用 ? 标记可选字段。编译为存在感知的持有者,而非简单的 *T

typescript
class Config {
  Host: string = "localhost";
  Port?: int;  // Go: Port targo.Optional[int]
}

读取语义:

  • config.Port !== undefined → 存在性检查
  • config.Port ?? 8080 → 缺失时回退

这与 Map.get()Array.find() 使用的 comma-ok 模式([T, bool])不同。

DefinedType

DefinedType 创建一个独立的 Go defined type——Targo 实现类型安全最重要的模式之一。

原始类型 DefinedType

typescript
class UserId extends DefinedType<int64>() {}
class Temperature extends DefinedType<float64>() {}

let id: UserId = 42 as UserId;       // Go: UserId(42)
let raw: int64 = id.underlying();    // Go: int64(id)

两个底层类型相同的 DefinedType 类不可互换。

基于 Struct 的 DefinedType

继承底层 struct 的字段但不继承方法:

typescript
class Point {
  X: int = 0; Y: int = 0;
  Distance(): float64 { return Math.sqrt(this.X * this.X + this.Y * this.Y); }
}

class Location extends DefinedType<Point>() {
  ToString(): string { return `(${this.X}, ${this.Y})`; }
}

let loc = new Location();
loc.X = 10;          // ✅ 字段继承
loc.ToString();      // ✅ 自有方法
// loc.Distance();   // ❌ 方法不继承

DefinedType 与集合

typescript
class UserList extends DefinedType<slice<User>>() {}
class Config extends DefinedType<map<string, string>>() {}

DefinedType 规则

  • 创建的是 Go 名义类型,不是 TypeScript 类型别名
  • 使用 as 进行转换:42 as UserId,反向使用 id.underlying()
  • 不能有 constructor——使用 as 或工厂函数
  • 支持嵌套 DefinedType:class OInt extends DefinedType<MyInt>() {}
  • Underlying<T> 约束匹配所有底层类型为 T 的类型

Embedding

Embedding 提供 Go 风格的结构体嵌入。被嵌入类型的字段和方法会提升到外层 struct。

基本用法

typescript
class Animal {
  Name: string = "";
}

class Dog extends Embedding<{ Animal: Animal }>() {
  Bark(): string {
    return `${this.Name} says woof`;  // Name 从 Animal 提升
  }
}

多重嵌入

typescript
class ReadWriter extends Embedding<{ Reader: Reader; Writer: Writer }>() {}

Embedding 规则

  • 键名必须匹配类型名:{ Reader: Reader } ✅,{ Foo: Reader }
  • 只有 struct 和 interface 可以被嵌入(不能嵌入 slice、map、chan 或原始类型)
  • 提升字段:直接访问 this.Name 或显式访问 this.Animal.Name
  • 命名冲突:如果两个嵌入类型有同名成员,该成员不会被提升——需要显式限定访问
  • 嵌入是组合,不是继承——没有多态分发

Struct Tags

使用 JSDoc 注释在 class 字段上添加 Go struct tag。

@json 简写用于简单 JSON tag:

typescript
class User {
  /** @json "user_name" */
  Name: string = "";

  /** @json "email,omitempty" */
  Email: string = "";

  /** @json "-" */
  #password: string = "";
}

@tag 完整语法用于多个 tag(反引号包裹):

typescript
class Product {
  /** @tag `json:"id" db:"product_id"` */
  Id: string = "";

  /** @tag `json:"name" xml:"Name" db:"product_name"` */
  Name: string = "";
}

接口

  • 接口是 Go 风格的行为契约
  • 涉及真实 Go interface 的代码使用 GoInterface 标记
  • 完整规则见 interfaces.md,包括 GoInterface、class 兼容性和方法形状要求

组合模式

需求使用
复用数据布局组合(另一个类型的字段)
从另一个 struct 提升字段/方法Embedding<{ T: T }>()
具有类型安全的独立名义类型DefinedType<T>()
行为契约 / 可替换性interface(+ Go 边界使用 GoInterface

常见错误

  • 以为支持 constructor 就意味着推荐 JavaScript OOP 模式——以 Go-first 思维
  • 试图用 extends 风格的层次设计代替组合
  • 使用 TypeScript private / protected 而不是 #field
  • 把两个底层类型相同的 DefinedType 当作可互换的
  • DefinedType 类添加 constructor(不允许)
  • 忘记 Embedding 会提升字段,可能导致命名冲突
  • 期望方法通过 DefinedType<Struct>() 继承——只有字段会继承
  • User 已经是指针时使用 Ptr<User>(class 实例默认就是 *T