Appearance
从 TypeScript 到 Targo
欢迎 TypeScript 开发者!本指南帮助你快速理解 Targo 与 TypeScript 的异同。
核心理念
Targo 使用 TypeScript 语法,但具有 Go 语义。可以理解为:
Targo = TypeScript 语法 + Go 类型系统 + Go 运行时这意味着:
- ✅ 你熟悉的语法:
let、const、箭头函数、解构等 - ⚠️ 不同的类型系统:明确的数值类型、值/引用语义
- ❌ 没有 JavaScript 运行时:没有
Promise、async/await、DOM API
快速对比
| 特性 | TypeScript | Targo |
|---|---|---|
| 数值类型 | number | int, float64, int32 等 |
| 数组 | Array<T> 或 T[] | slice<T> |
| 对象 | {} | class 或 interface |
| 异步 | Promise, async/await | chan, go() |
| 错误处理 | try/catch | 多返回值 [result, error] |
| 类继承 | extends | 组合(Embedding) |
| Null | null 和 undefined | 只有 null |
类型系统差异
1. 数值类型
TypeScript 只有一个 number 类型,Targo 需要明确指定:
typescript
// ❌ TypeScript(在 Targo 中不工作)
let count: number = 42;
let price: number = 19.99;
// ✅ Targo(必须明确类型)
let count: int = 42;
let price: float64 = 19.99;
let age: int8 = 25;
let port: uint16 = 8080;常用数值类型:
- 整数:
int,int8,int16,int32,int64 - 无符号:
uint,uint8,uint16,uint32,uint64 - 浮点:
float32,float64 - 大整数:
bigint
选择建议
- 默认使用
int和float64 - 需要节省内存时使用
int8、int16等 - 处理二进制数据时使用
uint8(byte)
2. 数组和集合
TypeScript 的 Array 变成 Targo 的 slice:
typescript
// ❌ TypeScript
let items: Array<string> = ["a", "b", "c"];
let nums: number[] = [1, 2, 3];
// ✅ Targo
let items: slice<string> = ["a", "b", "c"];
let nums: slice<int> = [1, 2, 3];
// 如果需要 JS 风格的方法(push, map, filter)
let arr: Array<int> = [1, 2, 3];
arr.push(4);
let doubled = arr.map(x => x * 2);集合类型对比:
| TypeScript | Targo | 说明 |
|---|---|---|
Array<T> | slice<T> | 动态数组 |
Map<K,V> | map<K,V> | 键值映射 |
Set<T> | ❌ | 使用 map<T, bool> 代替 |
| - | chan<T> | 通道(并发通信) |
| - | Fixed<T, N> | 固定大小数组 |
3. Null 和 Undefined
Targo 只有 null(映射到 Go 的 nil):
typescript
// ❌ TypeScript
let x: string | undefined = undefined;
let y: string | null = null;
// ✅ Targo(只有 null)
let x: string | null = null;
// 可选字段使用 ?
class Config {
host: string;
port?: int; // 等价于 port: int | null
}4. 布尔条件
Targo 没有 truthy/falsy 值,条件必须是 bool 类型:
typescript
// ❌ TypeScript(在 Targo 中不工作)
if (x) { } // x 必须是 bool
if (arr.length) { } // length 是 int,不是 bool
if (str) { } // str 是 string,不是 bool
// ✅ Targo(显式比较)
if (x != null) { }
if (arr.length > 0) { }
if (str != "") { }异步编程
Promise → Channel
TypeScript 使用 Promise 和 async/await,Targo 使用 chan 和 go():
typescript
// ❌ TypeScript
async function fetchData(url: string): Promise<string> {
const response = await fetch(url);
return response.text();
}
// 使用
const data = await fetchData("https://api.example.com");
// ✅ Targo
import { Get } from "net/http";
import { ReadAll } from "io";
function fetchData(url: string): chan<string> {
let ch = chan.make<string>();
go(() => {
let [resp, err] = Get(url);
if (err != null) {
ch.close();
return;
}
let [body, err2] = ReadAll(resp.Body);
resp.Body.Close();
if (err2 != null) {
ch.close();
return;
}
ch.send(string(body));
ch.close();
});
return ch;
}
// 使用
let ch = fetchData("https://api.example.com");
let data = ch.receive();并发模式对比
typescript
// TypeScript - Promise.all
const [user, posts, comments] = await Promise.all([
fetchUser(id),
fetchPosts(id),
fetchComments(id)
]);
// Targo - 使用 goroutine 和 channel
let userCh = chan.make<User>();
let postsCh = chan.make<slice<Post>>();
let commentsCh = chan.make<slice<Comment>>();
go(() => { userCh.send(fetchUser(id)); });
go(() => { postsCh.send(fetchPosts(id)); });
go(() => { commentsCh.send(fetchComments(id)); });
let user = userCh.receive();
let posts = postsCh.receive();
let comments = commentsCh.receive();类和接口
Class 是结构体
Targo 的 class 编译为 Go 结构体,不是 JavaScript 类:
typescript
// Targo
class Person {
name: string;
age: int;
greet(): string {
return `Hello, ${this.name}`;
}
}
// 创建实例(使用工厂函数)
function NewPerson(name: string, age: int): Person {
return { name, age } as Person;
}
let p = NewPerson("Alice", 30);
console.log(p.greet());关键差异:
- ❌ 没有
constructor,使用工厂函数 - ❌ 没有
extends(继承),使用组合 - ❌ 没有
private/protected关键字 - ✅ 使用
#field表示私有字段
组合代替继承
typescript
// ❌ TypeScript 继承
class Animal {
name: string;
speak(): void {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
bark(): void {
console.log("Woof!");
}
}
// ✅ Targo 组合
class Animal {
name: string;
speak(): void {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Embedding({ animal: {} as Animal }) {
bark(): void {
console.log("Woof!");
}
}
// 使用
let dog = new Dog();
dog.animal.name = "Buddy";
dog.animal.speak(); // 访问嵌入字段的方法
dog.bark();接口
接口工作方式类似,但具有 Go 的隐式实现:
typescript
// 定义接口
interface Writer {
write(data: slice<byte>): [int, error | null];
}
// 隐式实现(不需要 implements 关键字)
class FileWriter {
write(data: slice<byte>): [int, error | null] {
// 实现
return [data.len(), null];
}
}
// FileWriter 自动实现 Writer 接口
let w: Writer = new FileWriter();错误处理
Try/Catch → 多返回值
Targo 没有异常,使用多返回值处理错误:
typescript
// ❌ TypeScript
try {
const result = riskyOperation();
console.log(result);
} catch (error) {
console.error("Error:", error);
}
// ✅ Targo
import { New } from "errors";
function riskyOperation(): [int, error | null] {
// 模拟可能失败的操作
if (Math.random() > 0.5) {
return [0, New("operation failed")];
}
return [42, null];
}
// 使用
let [result, err] = riskyOperation();
if (err != null) {
console.error("Error:", err);
return;
}
console.log(result);错误处理模式
typescript
// 模式 1:立即返回错误
function processFile(path: string): error | null {
let [data, err] = readFile(path);
if (err != null) {
return err; // 传播错误
}
let [result, err2] = parseData(data);
if (err2 != null) {
return err2;
}
return null;
}
// 模式 2:包装错误
import { Errorf } from "fmt";
function loadConfig(path: string): [Config, error | null] {
let [data, err] = readFile(path);
if (err != null) {
return [zero<Config>(), Errorf("failed to load config: %w", err)];
}
// ...
return [config, null];
}模块系统
导入 Go 包
typescript
// TypeScript
import { readFile } from 'fs/promises';
import express from 'express';
// Targo - 导入 Go 标准库
import { Println, Printf } from "fmt";
import { ReadFile } from "os";
import { Get } from "net/http";
// Targo - 导入第三方 Go 包
import { NewRouter } from "github.com/gorilla/mux";包管理
typescript
// TypeScript - package.json
{
"dependencies": {
"express": "^4.18.0"
}
}
// Targo - go.mod
module myapp
go 1.22
require (
github.com/gorilla/mux v1.8.0
)不支持的特性
JavaScript 运行时
| 特性 | 状态 | Targo 替代 |
|---|---|---|
Promise | ❌ | chan<T> |
async/await | ❌ | go() + channel |
setTimeout | ❌ | time.Sleep() |
setInterval | ❌ | time.Ticker |
fetch | ❌ | net/http |
JSON.parse | ❌ | encoding/json |
console.log | ✅ | 映射到 fmt.Println |
TypeScript 高级特性
| 特性 | 状态 |
|---|---|
keyof | ❌ |
typeof(类型操作符) | ❌ |
| 条件类型 | ❌ |
| 映射类型 | ❌ |
| 模板字面量类型 | ❌ |
| 装饰器 | 🧪(仅 @tag) |
Class 特性
| 特性 | 状态 | Targo 替代 |
|---|---|---|
constructor | ❌ | 工厂函数 |
extends | ❌ | 组合(Embedding) |
private/protected | ❌ | #field |
abstract class | ❌ | 接口 |
static | ❌ | 包级函数 |
迁移检查清单
从 TypeScript 项目迁移到 Targo 时,按以下步骤检查:
类型相关
- [ ] 将
number替换为具体类型(int、float64等) - [ ] 将
Array<T>或T[]替换为slice<T> - [ ] 将
undefined统一为null - [ ] 移除
Set<T>,使用map<T, bool>代替
异步相关
- [ ] 将
Promise<T>替换为chan<T> - [ ] 移除
async/await,使用go()和 channel - [ ] 将
Promise.all()改为并发 goroutine
错误处理
- [ ] 将
try/catch替换为多返回值 - [ ] 添加错误检查:
if (err != null) { return err; } - [ ] 使用
error | null类型
条件和循环
- [ ] 使布尔条件显式化:
if (x)→if (x != null) - [ ] 检查数组长度:
if (arr.length)→if (arr.length > 0)
类和对象
- [ ] 将类继承改为组合(Embedding)
- [ ] 移除
constructor,创建工厂函数 - [ ] 使用
#field表示私有字段 - [ ] 移除
static方法,改为包级函数
导入和模块
- [ ] 更新导入语句为 Go 包路径
- [ ] 创建
go.mod文件 - [ ] 使用
targo bind生成 Go 包的类型声明
实战示例
HTTP 服务器
typescript
// TypeScript + Express
import express from 'express';
const app = express();
app.get('/hello', (req, res) => {
res.json({ message: 'Hello, World!' });
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
// Targo + net/http
import { HandleFunc, ListenAndServe } from "net/http";
import { Fprintf } from "fmt";
function helloHandler(w: ResponseWriter, r: Request): void {
Fprintf(w, `{"message": "Hello, World!"}`);
}
function main(): void {
HandleFunc("/hello", helloHandler);
console.log("Server running on port 3000");
ListenAndServe(":3000", null);
}数据处理
typescript
// TypeScript
interface User {
id: number;
name: string;
email: string;
}
async function getUsers(): Promise<User[]> {
const response = await fetch('/api/users');
return response.json();
}
const users = await getUsers();
const names = users.map(u => u.name);
const adults = users.filter(u => u.age >= 18);
// Targo
class User {
id: int;
name: string;
email: string;
}
function getUsers(): chan<slice<User>> {
let ch = chan.make<slice<User>>();
go(() => {
// 模拟 API 调用
let users = slice.of<User>(
{ id: 1, name: "Alice", email: "alice@example.com" } as User,
{ id: 2, name: "Bob", email: "bob@example.com" } as User
);
ch.send(users);
ch.close();
});
return ch;
}
let usersCh = getUsers();
let users = usersCh.receive();
// 使用 Array 获取 map/filter 方法
let usersArr = users as Array<User>;
let names = usersArr.map(u => u.name);
let adults = usersArr.filter(u => u.age >= 18);常见问题
Q: 为什么没有 number 类型?
A: Go 有多种数值类型(int, float64 等),每种都有不同的大小和精度。明确类型可以:
- 避免隐式转换导致的精度损失
- 更好的性能(使用合适的类型)
- 与 Go 生态系统兼容
Q: 为什么不支持 async/await?
A: Go 使用 goroutine 和 channel 实现并发,这是一种不同但更强大的模型:
- Goroutine 比 Promise 更轻量
- Channel 提供类型安全的通信
- 可以使用
select实现复杂的并发模式
Q: 如何处理 JSON?
A: 使用 Go 的 encoding/json 包:
typescript
import { Marshal, Unmarshal } from "encoding/json";
class User {
name: string;
age: int;
}
// 序列化
let user = { name: "Alice", age: 30 } as User;
let [jsonData, err] = Marshal(user);
// 反序列化
let user2 = zero<User>();
let err2 = Unmarshal(jsonData, ref(user2));Q: 可以使用 npm 包吗?
A: 不可以。Targo 编译为 Go 代码,只能使用 Go 包。但 Go 生态系统非常丰富,大多数需求都有对应的 Go 包。
下一步
- 常见模式对比 - 更多 TypeScript vs Targo 模式
- 类型系统 - 深入理解 Targo 类型系统
- 集合类型 - 掌握 slice、map、chan
- 并发编程 - 学习 goroutine 和 channel
参考资源
- TypeScript 官方文档
- Go 官方教程
- Effective Go - Go 最佳实践