类型系统
Targo 的类型系统是 Go-first 的,即使语法看起来像 TypeScript。
需要精确规则或实践边界时查阅此页。
核心规则
number不推荐;映射到float64。优先使用显式 Go 数值类型。- 支持
bool、string、byte、rune、complex64、complex128、bigint。 null和undefined是语言模型的一部分;两者都映射到 Go 的nil。- 条件必须是显式
bool。不要依赖 JavaScript truthy/falsy 行为。 - 混合类型算术是编译错误——使用显式转换(
f as int)。 - 无类型数值字面量会适配周围的类型上下文,匹配 Go 的无类型常量规则。
数值类型
所有 Go 数值类型都可作为一等 Targo 类型使用。
| 类型 | 大小 | 说明 |
|---|---|---|
int | 平台相关(32/64 位) | 默认整数类型 |
int8 | 1 字节 | −128 … 127 |
int16 | 2 字节 | −32 768 … 32 767 |
int32 | 4 字节 | −2³¹ … 2³¹−1 |
int64 | 8 字节 | −2⁶³ … 2⁶³−1 |
uint | 平台相关(32/64 位) | int 的无符号对应 |
uint8 | 1 字节 | 0 … 255 |
uint16 | 2 字节 | 0 … 65 535 |
uint32 | 4 字节 | 0 … 2³²−1 |
uint64 | 8 字节 | 0 … 2⁶⁴−1 |
float32 | 4 字节 | IEEE 754 单精度 |
float64 | 8 字节 | IEEE 754 双精度 |
complex64 | 8 字节 | 两个 float32 分量 |
complex128 | 16 字节 | 两个 float64 分量 |
byte | 1 字节 | uint8 的别名 |
rune | 4 字节 | int32 的别名,Unicode 码点 |
bigint | 任意精度 | 映射到 Go *big.Int |
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。你不需要自己写 *。
let u = new User(); // Go: u := &User{} (类型 *User)
u.Name = "Alice"; // 自动解引用Val<T> 用于显式值语义。 当你需要 struct 值(而非指针)时,包装类型:
let v: Val<User> = zero<User>(); // Go: var v UserPtr<T> 用于原始类型指针。 原始类型默认是值。需要指向原始类型的指针时使用 Ptr<T>:
let np: Ptr<int> = ref(42); // Go: np := new(int); *np = 42
np.value = 100; // Go: *np = 100ref() — 取地址。 将值转换为引用/指针:
let np: Ptr<int> = ref(42); // 原始类型 → Ptr<int>
let uRef: User = ref(userVal); // Val<User> → User (*User)deref() — 解引用。 将引用转换回值拷贝:
let copy: Val<User> = deref(u); // Go: copy := *uzero<T>() — 类型化零值(非 null):
let n = zero<int>(); // 0
let s = zero<string>(); // ""
let u = zero<User>(); // Val<User>,所有字段归零| Targo | Go | 语义 |
|---|---|---|
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。
let user: User | null = null; // Go: var user *User = nil
let name: string = ""; // 不能为 nullGo 映射: User | null → *User(可为 nil),slice<T> | null → []T(可为 nil),map<K,V> | null → map[K]V(可为 nil),Ptr<int> | null → *int(可为 nil)。
安全访问运算符。 ?.(可选链)和 ??(空值合并)按预期工作:
let name = user?.Name ?? "unknown";unsafeNil<T>() — 在类型签名不允许 null 的地方传递 nil。 某些 Go API 接受非可空类型但将 nil 视为有效值(例如 nil *Config 表示"使用默认值",nil []byte 表示"无输入")。由于 Targo 的类型系统不允许向非可空参数传递 null,unsafeNil<T>() 是逃生通道:
// 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遵循 Golen()—— 字节长度,不是 Unicode 字符数。- 使用
runes(s)获取slice<rune>进行字符级迭代。
类型转换
Targo 使用 as 进行类型转换和类型断言,使用 instanceof 进行安全类型检查。
数值转换使用 as:
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:
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)从接口进行类型断言:
let r: Reader = getReader();
let f = r as File; // Go: r.(File) — 类型错误时 panic类型断言可能在运行时 panic。优先使用 instanceof 进行安全检查。
instanceof — 安全类型检查:
if (r instanceof File) {
// r 在此分支中被收窄为 File
r.Close();
}连续的 instanceof 检查编译为 Go type switch。
泛型约束
Targo 提供内置约束类型,映射到 Go 泛型约束。
| 约束 | 包含 | Go 等价 |
|---|---|---|
comparable | 支持 == / != 的类型 | comparable |
Ordered | 所有数值 + string 类型(支持 < >) | cmp.Ordered |
Signed | int、int8 … int64 及其底层类型 | ~int | ~int8 | … | ~int64 |
Unsigned | uint、uint8 … uint64、uintptr 及其底层类型 | ~uint | ~uint8 | … | ~uint64 | ~uintptr |
Integer | Signed | Unsigned | 所有整数类型 |
Float | float32、float64 及其底层类型 | ~float32 | ~float64 |
Complex | complex64、complex128 及其底层类型 | ~complex64 | ~complex128 |
Numeric | Integer | Float | Complex | 所有数值类型 |
Underlying<T> | 所有底层类型为 T 的类型 | ~T |
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 检查。
禁止: 属性访问、方法调用、算术运算、索引、迭代、展开、一元运算。
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 数值类型(int32、uint16、…) |
| 浮点数学 | float64(或内存敏感时用 float32) |
| 任意精度整数 | bigint |
| 边界处的可空结果 | T | null 或元组返回 |
| 按值传递 struct | Val<T> |
| 原始类型指针 | Ptr<T> |
常见错误
- 出于 TypeScript 习惯到处使用
number——优先用int或float64。 - 在条件中假设 truthy/falsy 行为(
if (str)而不是if (str != ""))。 - 忘记
string.length是字节长度,不是字符数。 - 在算术运算中混合数值类型而不做显式转换。
- 使用
any后期望 TypeScript 风格的属性访问——先收窄。 - 当 API 实际接受
T | null时使用unsafeNil<T>()——直接传null即可。 - 忘记
class实例已经是指针——Ptr<User>几乎不是你想要的。 - 把
Val<User>和User当作可互换的——它们有不同的语义(值拷贝 vs 共享引用)。