Skip to content

使用 Go 包

本指南介绍如何在 Targo 中使用 Go 标准库和第三方包。

目标读众

  • 想要使用 Go 生态系统的开发者
  • 需要访问 Go 标准库的开发者
  • 想要集成第三方 Go 包的开发者

核心概念

Targo 通过 binder 工具生成 TypeScript 类型声明(.d.ts 文件),使你能够在 Targo 代码中使用 Go 包:

  • targo bind - 生成类型声明的命令
  • .targo/ - 存放生成的类型声明
  • 自动导入 - TypeScript 编译器自动识别类型

targo bind 命令

基本用法

使用 targo bind 命令生成 Go 包的类型声明:

bash
# 绑定单个包
targo bind fmt

# 绑定多个包
targo bind fmt os strings

# 绑定第三方包
targo bind github.com/user/package

输出结构

绑定后会在 .targo/ 目录下生成类型声明:

.targo/
├── fmt/
│   └── fmt.d.ts
├── os/
│   └── os.d.ts
└── strings/
    └── strings.d.ts

常用选项

bash
# 指定输出目录
targo bind fmt --output ./types

# 绑定标准库包
targo bind --stdlib fmt os strings

使用 Go 标准库

1. fmt - 格式化输出

bash
# 绑定 fmt 包
targo bind fmt
typescript
import { fmt } from "fmt";

// 格式化字符串
let msg = fmt.Sprintf("Hello, %s!", "World");
console.log(msg);  // "Hello, World!"

// 打印到标准输出
fmt.Println("Hello, World!");
fmt.Printf("Number: %d\n", 42);

// 格式化错误
let err = fmt.Errorf("failed to open file: %s", filename);

2. os - 操作系统接口

bash
# 绑定 os 包
targo bind os
typescript
import { os } from "os";

// 读取文件
let [data, err] = os.ReadFile("config.json");
if (err != null) {
    console.log(`Error: ${err.Error()}`);
    return;
}
console.log(string(data));

// 写入文件
let content = Slice.from("Hello, World!");
let err2 = os.WriteFile("output.txt", content, 0644);
if (err2 != null) {
    console.log(`Error: ${err2.Error()}`);
}

// 环境变量
let home = os.Getenv("HOME");
os.Setenv("MY_VAR", "value");

// 文件操作
let [file, err3] = os.Open("data.txt");
if (err3 != null) {
    return;
}
defer(() => file.Close());

3. strings - 字符串操作

bash
# 绑定 strings 包
targo bind strings
typescript
import { strings } from "strings";

// 字符串操作
let upper = strings.ToUpper("hello");  // "HELLO"
let lower = strings.ToLower("WORLD");  // "world"

// 分割和连接
let parts = strings.Split("a,b,c", ",");  // ["a", "b", "c"]
let joined = strings.Join(parts, "-");    // "a-b-c"

// 查找和替换
let contains = strings.Contains("hello", "ell");  // true
let replaced = strings.Replace("hello", "l", "L", -1);  // "heLLo"

// 修剪
let trimmed = strings.TrimSpace("  hello  ");  // "hello"

4. http - HTTP 客户端和服务器

bash
# 绑定 http 包
targo bind net/http

HTTP 客户端

typescript
import { http } from "net/http";
import { io } from "io";

// GET 请求
let [resp, err] = http.Get("https://api.github.com");
if (err != null) {
    console.log(`Error: ${err.Error()}`);
    return;
}
defer(() => resp.Body.Close());

// 读取响应
let [body, err2] = io.ReadAll(resp.Body);
if (err2 != null) {
    console.log(`Error: ${err2.Error()}`);
    return;
}

console.log(`Status: ${resp.StatusCode}`);
console.log(`Body: ${string(body)}`);

HTTP 服务器

typescript
import { http } from "net/http";
import { fmt } from "fmt";

function handler(w: http.ResponseWriter, r: http.Request): void {
    fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]);
}

http.HandleFunc("/", handler);
console.log("Server starting on :8080");
http.ListenAndServe(":8080", null);

5. json - JSON 编解码

bash
# 绑定 json 包
targo bind encoding/json
typescript
import { json } from "encoding/json";

interface User {
    ID: int;
    Name: string;
    Email: string;
}

// 编码(序列化)
let user: User = { ID: 1, Name: "Alice", Email: "alice@example.com" };
let [data, err] = json.Marshal(user);
if (err != null) {
    console.log(`Error: ${err.Error()}`);
    return;
}
console.log(string(data));

// 解码(反序列化)
let jsonStr = '{"ID":1,"Name":"Alice","Email":"alice@example.com"}';
let user2: User = zero<User>();
let err2 = json.Unmarshal(Slice.from(jsonStr), ref(user2));
if (err2 != null) {
    console.log(`Error: ${err2.Error()}`);
    return;
}
console.log(user2.Name);  // "Alice"

6. time - 时间和日期

bash
# 绑定 time 包
targo bind time
typescript
import { time } from "time";

// 当前时间
let now = time.Now();
console.log(now.String());

// 格式化时间
let formatted = now.Format(time.RFC3339);
console.log(formatted);  // "2024-01-01T12:00:00Z"

// 解析时间
let [t, err] = time.Parse(time.RFC3339, "2024-01-01T12:00:00Z");
if (err != null) {
    console.log(`Error: ${err.Error()}`);
}

// 时间运算
let tomorrow = now.Add(24 * time.Hour);
let duration = tomorrow.Sub(now);
console.log(duration.Hours());  // 24

// 睡眠
time.Sleep(1 * time.Second);

// 定时器
let timer = time.NewTimer(5 * time.Second);
timer.C.receive();  // 等待 5 秒
console.log("5 seconds passed");

使用第三方包

1. 安装 Go 包

首先使用 go get 安装 Go 包:

bash
# 安装第三方包
go get github.com/gorilla/mux
go get github.com/lib/pq

2. 绑定包

使用 targo bind 生成类型声明:

bash
# 绑定第三方包
targo bind github.com/gorilla/mux

3. 使用包

typescript
import { mux } from "github.com/gorilla/mux";
import { http } from "net/http";

// 创建路由器
let r = mux.NewRouter();

// 定义路由
r.HandleFunc("/", (w, req) => {
    w.Write(Slice.from("Home Page"));
});

r.HandleFunc("/users/{id}", (w, req) => {
    let vars = mux.Vars(req);
    let id = vars["id"];
    w.Write(Slice.from(`User ID: ${id}`));
});

// 启动服务器
http.ListenAndServe(":8080", r);

常用包示例

文件路径操作

bash
targo bind path/filepath
typescript
import { filepath } from "path/filepath";

// 路径操作
let joined = filepath.Join("dir", "subdir", "file.txt");
let dir = filepath.Dir("/path/to/file.txt");  // "/path/to"
let base = filepath.Base("/path/to/file.txt");  // "file.txt"
let ext = filepath.Ext("file.txt");  // ".txt"

// 路径匹配
let [matched, err] = filepath.Match("*.txt", "file.txt");
if (matched) {
    console.log("Matched!");
}

// 遍历目录
filepath.Walk("/path/to/dir", (path, info, err) => {
    if (err != null) {
        return err;
    }
    console.log(path);
    return null;
});

正则表达式

bash
targo bind regexp
typescript
import { regexp } from "regexp";

// 编译正则表达式
let [re, err] = regexp.Compile(`\d+`);
if (err != null) {
    console.log(`Error: ${err.Error()}`);
    return;
}

// 匹配
let matched = re.MatchString("abc123");  // true
let found = re.FindString("abc123def");  // "123"
let all = re.FindAllString("a1b2c3", -1);  // ["1", "2", "3"]

// 替换
let replaced = re.ReplaceAllString("a1b2c3", "X");  // "aXbXcX"

数据库(PostgreSQL)

bash
go get github.com/lib/pq
targo bind database/sql
targo bind github.com/lib/pq
typescript
import { sql } from "database/sql";
import _ from "github.com/lib/pq";  // 导入驱动

// 连接数据库
let [db, err] = sql.Open("postgres", "user=postgres dbname=mydb sslmode=disable");
if (err != null) {
    console.log(`Error: ${err.Error()}`);
    return;
}
defer(() => db.Close());

// 查询
let [rows, err2] = db.Query("SELECT id, name FROM users WHERE age > $1", 18);
if (err2 != null) {
    console.log(`Error: ${err2.Error()}`);
    return;
}
defer(() => rows.Close());

// 遍历结果
while (rows.Next()) {
    let id: int = 0;
    let name: string = "";
    let err3 = rows.Scan(ref(id), ref(name));
    if (err3 != null) {
        console.log(`Error: ${err3.Error()}`);
        continue;
    }
    console.log(`${id}: ${name}`);
}

// 插入
let [result, err4] = db.Exec("INSERT INTO users (name, age) VALUES ($1, $2)", "Alice", 25);
if (err4 != null) {
    console.log(`Error: ${err4.Error()}`);
}

类型映射

Go 类型到 Targo 类型

Go 类型Targo 类型说明
int, int64int, int64整数
float64float64浮点数
stringstring字符串
boolbool布尔值
[]Tslice<T>切片
[N]TFixed<T, N>数组
map[K]Vmap<K, V>映射
chan Tchan<T>通道
*TT指针(默认引用)
interface{}any空接口
errorerror | null错误

函数签名

Go 函数的多返回值映射为 Targo 的元组:

go
// Go
func ReadFile(name string) ([]byte, error)
typescript
// Targo
function ReadFile(name: string): [slice<byte>, error | null]

实际示例

REST API 服务器

typescript
import { http } from "net/http";
import { json } from "encoding/json";
import { fmt } from "fmt";

interface User {
    ID: int;
    Name: string;
    Email: string;
}

let users: User[] = [
    { ID: 1, Name: "Alice", Email: "alice@example.com" },
    { ID: 2, Name: "Bob", Email: "bob@example.com" }
];

function getUsersHandler(w: http.ResponseWriter, r: http.Request): void {
    w.Header().Set("Content-Type", "application/json");
    json.NewEncoder(w).Encode(users);
}

function getUserHandler(w: http.ResponseWriter, r: http.Request): void {
    let id = r.URL.Query().Get("id");
    if (id == "") {
        http.Error(w, "id is required", 400);
        return;
    }
    
    let userId = parseInt(id);
    for (const user of users) {
        if (user.ID == userId) {
            w.Header().Set("Content-Type", "application/json");
            json.NewEncoder(w).Encode(user);
            return;
        }
    }
    
    http.Error(w, "user not found", 404);
}

http.HandleFunc("/users", getUsersHandler);
http.HandleFunc("/user", getUserHandler);

fmt.Println("Server starting on :8080");
http.ListenAndServe(":8080", null);

文件处理工具

typescript
import { os } from "os";
import { filepath } from "path/filepath";
import { strings } from "strings";

function processFiles(dir: string, ext: string): error | null {
    return filepath.Walk(dir, (path, info, err) => {
        if (err != null) {
            return err;
        }
        
        if (info.IsDir()) {
            return null;
        }
        
        if (!strings.HasSuffix(path, ext)) {
            return null;
        }
        
        console.log(`Processing: ${path}`);
        
        let [data, err2] = os.ReadFile(path);
        if (err2 != null) {
            return err2;
        }
        
        // 处理文件内容...
        let processed = process(data);
        
        let err3 = os.WriteFile(path, processed, info.Mode());
        if (err3 != null) {
            return err3;
        }
        
        return null;
    });
}

// 使用
let err = processFiles("./src", ".txt");
if (err != null) {
    console.log(`Error: ${err.Error()}`);
}

常见陷阱

1. 忘记绑定包

typescript
// ❌ 错误:未绑定 fmt 包
import { fmt } from "fmt";  // 类型错误

// ✅ 正确:先绑定包
// 在终端运行:targo bind fmt
import { fmt } from "fmt";

2. 错误的导入路径

typescript
// ❌ 错误:使用 Go 导入路径
import { http } from "net/http";  // 正确

// ❌ 错误:使用错误的路径
import { http } from "http";  // 错误

3. 指针类型混淆

typescript
// Go: func Open(name string) (*File, error)
// Targo: function Open(name: string): [File, error | null]

// ✅ 正确:File 已经是引用类型
let [file, err] = os.Open("file.txt");

// ❌ 错误:不需要额外的指针
let [file, err] = os.Open("file.txt");
let ptr = ref(file);  // 不需要

最佳实践

  1. 按需绑定包

    • 只绑定实际使用的包
    • 减少类型声明文件大小
  2. 使用标准库

    • 优先使用 Go 标准库
    • 标准库经过充分测试
  3. 检查错误

    • 始终检查 Go 函数返回的错误
    • 使用 error | null 类型
  4. 使用 defer 清理资源

    • 文件、连接等资源需要关闭
    • 使用 defer 确保清理
  5. 阅读 Go 文档

    • 参考 Go 官方文档了解包的用法
    • 类型声明与 Go API 一致

下一步

参考