Skip to content

类型系统

Targo 的类型系统基于 Go,但使用 TypeScript 风格的语法。本指南帮助你理解 Targo 的类型系统及其与 TypeScript 的差异。

核心设计原则

  1. Go 类型为基础 - 所有类型直接映射到 Go 类型
  2. TypeScript 语法 - 使用熟悉的 TS 语法表达
  3. 零运行时开销 - 类型在编译时完全消除
  4. 显式转换 - 值/引用转换必须显式进行

数值类型

TypeScript vs Targo

TypeScript 只有一个 number 类型,而 Targo 需要明确的数值类型:

typescript
let x: number = 42;
let y: number = 3.14;
let z: number = 255;
typescript
let x: int = 42;
let y: float64 = 3.14;
let z: uint8 = 255;

整数类型

Targo 类型大小范围说明
int平台相关有符号最常用的整数类型
int81 字节-128 到 127小范围整数
int162 字节-32,768 到 32,767
int324 字节-2³¹ 到 2³¹-1
int648 字节-2⁶³ 到 2⁶³-1大范围整数
uint平台相关无符号无符号整数
uint81 字节0 到 255字节值
uint162 字节0 到 65,535
uint324 字节0 到 2³²-1
uint648 字节0 到 2⁶⁴-1
byte1 字节0 到 255uint8 的别名
rune4 字节Unicode 码点int32 的别名
typescript
let count: int = 42;
let age: int8 = 25;
let port: uint16 = 8080;
let char: rune = 'A';

浮点类型

Targo 类型大小精度
float324 字节单精度
float648 字节双精度(推荐)
typescript
let pi: float64 = 3.14159;
let temperature: float32 = 36.5;

其他基础类型

typescript
let name: string = "Targo";
let isValid: bool = true;
let huge: bigint = 123456789012345678901234567890n;

值语义 vs 引用语义

这是 Targo 最重要的概念之一。

基本规则

类型默认语义Go 映射
原始类型 (int, string, etc.)值语义直接值
class引用语义指针 (*T)
Val<T>值语义值类型
Ptr<T>引用语义指针

Class 默认是引用

typescript
class User {
  name: string;
  age: int;
}

let user1 = new User();  // User 类型,实际是 *User
user1.name = "Alice";

let user2 = user1;       // 引用赋值
user2.name = "Bob";      // 修改 user1 和 user2

console.log(user1.name); // "Bob" - 两者指向同一对象
go
// 编译为 Go
type User struct {
    Name string
    Age  int
}

user1 := new(User)
user1.Name = "Alice"

user2 := user1
user2.Name = "Bob"

fmt.Println(user1.Name) // "Bob"

原始类型是值

typescript
let x: int = 42;
let y = x;      // 值复制
y = 100;        // 只修改 y

console.log(x); // 42 - x 不受影响

显式转换 API

ref() - 取地址

将值转换为引用/指针:

typescript
// 原始类型 → 指针
let n: int = 42;
let np: Ptr<int> = ref(n);
np.value = 100;

// 值类型 → 引用
let userVal: Val<User> = zero<User>();
let userRef: User = ref(userVal);

deref() - 解引用

将引用转换为值:

typescript
let user = new User();           // User (引用)
let userVal: Val<User> = deref(user);  // Val<User> (值)
userVal.name = "Bob";            // 修改副本,不影响 user

zero<T>() - 零值

获取类型的零值(非 null):

typescript
let n = zero<int>();        // 0
let s = zero<string>();     // ""
let b = zero<bool>();       // false
let u = zero<User>();       // Val<User>,所有字段为零值
go
// 编译为 Go
var n int
var s string
var b bool
var u User

zeroNil<T>() - 可空零值

获取可空的零值:

typescript
let arr = zeroNil<slice<int>>();  // slice<int> | null
let m = zeroNil<map<string, int>>(); // map<string, int> | null
let u = zeroNil<User>();          // User | null
go
// 编译为 Go
var arr []int      // nil
var m map[string]int  // nil
var u *User        // nil

可空类型

基本语法

typescript
// 可空类型
let user: User | null = null;
let count: int | null = null;  // ❌ 错误:原始类型不能直接为 null

// 可选字段(语法糖)
class Config {
  host: string;
  port?: int;  // 等价于 port: int | null
}

空安全访问

typescript
let user: User | null = getUser();

// 检查 null
if (user == null) {
  console.log("User not found");
  return;
}

// 此处 user 自动收窄为非空类型
console.log(user.name);

// 可选链(如果支持)
user?.name;

指针类型

原始类型指针

原始类型需要显式指针:

typescript
let np: Ptr<int> = ref(42);

// 读取
let v = np.value;  // 42

// 赋值
np.value = 100;

// 可空指针
let maybeInt: Ptr<int> | null = null;
if (maybeInt != null) {
  console.log(maybeInt.value);
}

Object 指针

Class 默认就是引用,不需要显式 Ptr<T>

typescript
let user = new User();  // 已经是指针
user.name = "Alice";    // 自动解引用

// 可空引用
let maybeUser: User | null = null;

类型转换

数值类型转换

typescript
let f: float64 = 3.14;
let n = f as int;           // 截断为整数

let i: int = 42;
let f2 = i as float64;      // 转换为浮点数

let small: int8 = 100;
let big = small as int64;   // 扩展精度

字符串转换

typescript
let s: string = "hello";
let bytes = s as slice<byte>;   // 转换为字节切片
let runes = s as slice<rune>;   // 转换为 rune 切片

let data: slice<byte> = slice.of(72, 105);
let str = data as string;       // 转换为字符串

类型别名和新类型

类型别名

typescript
type ID = int64;
type Handler = (req: Request, res: Response) => void;

let userId: ID = 12345;

新类型(Defined Type)

创建全新的类型,与底层类型不兼容:

typescript
class UserID extends DefinedType<int64> {}
class Celsius extends DefinedType<float64> {}

// 使用 as 创建实例
const id = 12345 as UserID;
const temp = 36.5 as Celsius;

// 类型安全:不能互相赋值
const id2: UserID = temp;  // ❌ 类型错误
go
// 编译为 Go
type UserID int64
type Celsius float64

id := UserID(12345)
temp := Celsius(36.5)

函数类型

基本函数类型

typescript
type Handler = (req: Request, res: Response) => void;
type Comparator = (a: int, b: int) => int;

多返回值

typescript
// 函数签名
function divmod(a: int, b: int): [int, int] {
  return [a / b, a % b];
}

// 使用
let [quotient, remainder] = divmod(10, 3);
go
// 编译为 Go(零开销)
func divmod(a, b int) (int, int) {
    return a / b, a % b
}

quotient, remainder := divmod(10, 3)

与 TypeScript 的对比

相同点

  • ✅ 类型注解语法 (: Type)
  • ✅ 类型推断
  • ✅ 联合类型 (T | U)
  • ✅ 泛型 (<T>)
  • ✅ 类型别名 (type)

主要差异

特性TypeScriptTargo
数值类型numberint, float64, etc.
数组Array<T>T[]slice<T>
null vs undefined两者都有只有 null
Class 语义引用(JS 对象)引用(Go 指针)
类型转换隐式显式
零值undefined类型特定的零值

实用示例

示例 1: 用户管理

typescript
class User {
  id: int64;
  name: string;
  email: string;
  age?: int;  // 可选字段
}

function createUser(name: string, email: string): User {
  let user = new User();
  user.id = generateID();
  user.name = name;
  user.email = email;
  // age 默认为 null
  return user;
}

function findUser(id: int64): User | null {
  // 查找用户...
  return zeroNil<User>();  // 未找到
}

// 使用
let user = findUser(123);
if (user != null) {
  console.log(`Found: ${user.name}`);
  if (user.age != null) {
    console.log(`Age: ${user.age}`);
  }
}

示例 2: 计数器

typescript
class Counter {
  value: int;
  
  increment(): void {
    this.value++;
  }
  
  getValue(): int {
    return this.value;
  }
}

// 值语义版本
let counterVal: Val<Counter> = zero<Counter>();
counterVal.value = 10;
let copy = counterVal;  // 值复制
copy.value = 20;
console.log(counterVal.value);  // 10 - 不受影响

// 引用语义版本
let counter = new Counter();
counter.value = 10;
let ref = counter;  // 引用赋值
ref.value = 20;
console.log(counter.value);  // 20 - 共享状态

常见陷阱

陷阱 1: 忘记类型注解

typescript
// ❌ 错误:Targo 需要明确类型
let x = 42;  // 类型推断可能不符合预期

// ✅ 正确:明确指定类型
let x: int = 42;

陷阱 2: 混淆值和引用

typescript
// ❌ 错误:期望修改原对象
let user = new User();
let userVal = deref(user);  // 创建副本
userVal.name = "Bob";       // 只修改副本

// ✅ 正确:使用引用
let user = new User();
let userRef = user;         // 引用赋值
userRef.name = "Bob";       // 修改原对象

陷阱 3: null 检查

typescript
// ❌ 错误:TypeScript 风格
if (user) {  // Targo 要求显式 bool
  // ...
}

// ✅ 正确:显式检查
if (user != null) {
  // ...
}

最佳实践

  1. 优先使用 intfloat64 - 除非有特定需求
  2. 明确值/引用语义 - 理解何时使用 Val<T>Ptr<T>
  3. 使用 zero<T>() 初始化 - 避免 null 检查
  4. 显式类型转换 - 不要依赖隐式转换
  5. 检查 null - 始终显式检查可空类型

下一步

参考