Skip to content

类型系统

Targo 的类型系统是 Go-first 的,即使语法看起来像 TypeScript。

需要精确规则或实践边界时查阅此页。

核心规则

  • number 不推荐;映射到 float64。优先使用显式 Go 数值类型。
  • 支持 boolstringbyterunecomplex64complex128bigint
  • nullundefined 是语言模型的一部分;两者都映射到 Go 的 nil
  • 条件必须是显式 bool。不要依赖 JavaScript truthy/falsy 行为。
  • 混合类型算术是编译错误——使用显式转换(f as int)。
  • 无类型数值字面量会适配周围的类型上下文,匹配 Go 的无类型常量规则。

数值类型

所有 Go 数值类型都可作为一等 Targo 类型使用。

类型大小说明
int平台相关(32/64 位)默认整数类型
int81 字节−128 … 127
int162 字节−32 768 … 32 767
int324 字节−2³¹ … 2³¹−1
int648 字节−2⁶³ … 2⁶³−1
uint平台相关(32/64 位)int 的无符号对应
uint81 字节0 … 255
uint162 字节0 … 65 535
uint324 字节0 … 2³²−1
uint648 字节0 … 2⁶⁴−1
float324 字节IEEE 754 单精度
float648 字节IEEE 754 双精度
complex648 字节两个 float32 分量
complex12816 字节两个 float64 分量
byte1 字节uint8 的别名
rune4 字节int32 的别名,Unicode 码点
bigint任意精度映射到 Go *big.Int
typescript
let count: int = 42;
let port: uint16 = 8080;
let pi: float64 = 3.14159;
let huge: bigint = 123456789012345678901234567890n;

不带小数点的整数字面量默认为 int。带小数点或指数的字面量默认为 float64

值语义与引用语义

Targo 区分值类型和引用类型,匹配 Go 的模型。

class → 默认指针。 class User 变量在 Go 中是 *User。你不需要自己写 *

typescript
let u = new User();   // Go: u := &User{}  (类型 *User)
u.Name = "Alice";     // 自动解引用

Val<T> 用于显式值语义。 当你需要 struct 值(而非指针)时,包装类型:

typescript
let v: Val<User> = zero<User>();  // Go: var v User

Ptr<T> 用于原始类型指针。 原始类型默认是值。需要指向原始类型的指针时使用 Ptr<T>

typescript
let np: Ptr<int> = ref(42);  // Go: np := new(int); *np = 42
np.value = 100;               // Go: *np = 100

ref() — 取地址。 将值转换为引用/指针:

typescript
let np: Ptr<int> = ref(42);      // 原始类型 → Ptr<int>
let uRef: User = ref(userVal);   // Val<User> → User (*User)

deref() — 解引用。 将引用转换回值拷贝:

typescript
let copy: Val<User> = deref(u);  // Go: copy := *u

zero<T>() — 类型化零值(非 null):

typescript
let n = zero<int>();       // 0
let s = zero<string>();    // ""
let u = zero<User>();      // Val<User>,所有字段归零
TargoGo语义
User(class)*User引用(指针)
Val<User>User值(struct 拷贝)
Ptr<int>*int原始类型指针
ref(x)&x / new(T)取地址
deref(p)*p解引用
zero<T>()var x T零值

可空性与 Nil 安全

T | null 将类型标记为可空。 没有 | null 的变量不能为 nil。

typescript
let user: User | null = null;   // Go: var user *User = nil
let name: string = "";          // 不能为 null

Go 映射: User | null*User(可为 nil),slice<T> | null[]T(可为 nil),map<K,V> | nullmap[K]V(可为 nil),Ptr<int> | null*int(可为 nil)。

安全访问运算符。 ?.(可选链)和 ??(空值合并)按预期工作:

typescript
let name = user?.Name ?? "unknown";

unsafeNil<T>() — 在类型签名不允许 null 的地方传递 nil。 某些 Go API 接受非可空类型但将 nil 视为有效值(例如 nil *Config 表示"使用默认值",nil []byte 表示"无输入")。由于 Targo 的类型系统不允许向非可空参数传递 nullunsafeNil<T>() 是逃生通道:

typescript
// Go: quick.CheckEqual(f, g, nil) — nil config 表示默认
// Targo 签名: CheckEqual(f, g, config: quick.Config) — 无 | null
const err = quick.CheckEqual(f, g, unsafeNil<quick.Config>());

// Go: hasher.Sum(nil) — nil []byte 表示不追加
const hash = hasher.Sum(unsafeNil<slice<byte>>());

仅在你确定 Go API 接受该参数为 nil 时使用 unsafeNil<T>()。它仅限于引用类型(slice、map、chan、Ptr、GoInterface、function、class)。

字符串

  • string.length 遵循 Go len() —— 字节长度,不是 Unicode 字符数。
  • 使用 runes(s) 获取 slice<rune> 进行字符级迭代。

类型转换

Targo 使用 as 进行类型转换和类型断言,使用 instanceof 进行安全类型检查。

数值转换使用 as

typescript
let f: float64 = 3.14;
let n = f as int;          // Go: int(f) — 截断
let big = n as int64;      // Go: int64(n)

String ↔ byte/rune slice:

typescript
let b = s as slice<byte>;   // Go: []byte(s)
let r = s as slice<rune>;   // Go: []rune(s)
let s2 = b as string;       // Go: string(b)

从接口进行类型断言:

typescript
let r: Reader = getReader();
let f = r as File;           // Go: r.(File) — 类型错误时 panic

类型断言可能在运行时 panic。优先使用 instanceof 进行安全检查。

instanceof — 安全类型检查:

typescript
if (r instanceof File) {
  // r 在此分支中被收窄为 File
  r.Close();
}

连续的 instanceof 检查编译为 Go type switch

泛型约束

Targo 提供内置约束类型,映射到 Go 泛型约束。

约束包含Go 等价
comparable支持 == / != 的类型comparable
Ordered所有数值 + string 类型(支持 < >cmp.Ordered
Signedintint8int64 及其底层类型~int | ~int8 | … | ~int64
Unsigneduintuint8uint64uintptr 及其底层类型~uint | ~uint8 | … | ~uint64 | ~uintptr
IntegerSigned | Unsigned所有整数类型
Floatfloat32float64 及其底层类型~float32 | ~float64
Complexcomplex64complex128 及其底层类型~complex64 | ~complex128
NumericInteger | Float | Complex所有数值类型
Underlying<T>所有底层类型为 T 的类型~T
typescript
function abs<T extends Signed>(x: T): T {
  if (x < 0) return -x as T;
  return x;
}

function maxOf<T extends Ordered>(a: T, b: T): T {
  return a > b ? a : b;
}

any 类型

any 映射到 Go 的 interface{}。与 TypeScript 的 any 不同,它是类型安全的——你不能在不收窄的情况下对 any 值做任何操作。

允许: 赋值、传递给 any 参数、== / != 比较、as T 断言、instanceof 检查。

禁止: 属性访问、方法调用、算术运算、索引、迭代、展开、一元运算。

typescript
function process(val: any): string {
  // val.toString()  — ❌ 禁止
  // val + 1         — ❌ 禁止
  if (val instanceof string) {
    return val;       // ✅ 收窄为 string
  }
  return val as string; // ✅ 断言(可能 panic)
}

把 Targo 的 any 理解为 TypeScript 的 unknown:使用前先收窄。

实践选择

需求优先选择
通用整数业务逻辑int
显式宽度 / 协议布局具体 Go 数值类型(int32uint16、…)
浮点数学float64(或内存敏感时用 float32
任意精度整数bigint
边界处的可空结果T | null 或元组返回
按值传递 structVal<T>
原始类型指针Ptr<T>

常见错误

  • 出于 TypeScript 习惯到处使用 number——优先用 intfloat64
  • 在条件中假设 truthy/falsy 行为(if (str) 而不是 if (str != ""))。
  • 忘记 string.length 是字节长度,不是字符数。
  • 在算术运算中混合数值类型而不做显式转换。
  • 使用 any 后期望 TypeScript 风格的属性访问——先收窄。
  • 当 API 实际接受 T | null 时使用 unsafeNil<T>()——直接传 null 即可。
  • 忘记 class 实例已经是指针——Ptr<User> 几乎不是你想要的。
  • Val<User>User 当作可互换的——它们有不同的语义(值拷贝 vs 共享引用)。