Playground: golang-enum-to-ts-playground.vercel.app/
Code: github.com/anc95/golan…
看到这个标题,你也许会觉得很奇怪,我为什么要把 Golang 的 enum 转换成 Typescript 呢?它的实际价值是什么?下面我会介绍一下最初要做这个的动机和想法
动机
在应用开发中,enum 数据类型是被大量使用的,我们可以用 enum 来表示一个任务的状态(todo、pending、done),也可以用 enum 来表示一些有限的选项(如表示学历),也可以用 enum 来表示简单的二元值(如性别、或者 Y/N)
在后端开发中,会在代码里直接使用 enum 变量,同时数据库里存储的往往是整形(如 0、1、2),作为开发者无需直接和 0、1 等这样的语言的数据打交道,而是使用 enum ,这使得代码可读性提高, 如对于任务状态用 Golang 可以这样表示
package task
type TaskStatus int
const (
Todo TaskStatus = iota
Pending
Done
)
现在,我们引入前端,因为数据库里存的是 0、1这样的数据,那前端拿到的 API 返回值往往也是这样子的,如
{
todos: [
{name: "Learning", status: 0}
]
}
这里的 status 0 表示的是什么状态呢?前端往往需要自己写一个 enum 类型,来进行转换
enum TaskStatus {
Todo,
Pending,
Done
}
也就是说,前后端需要各自维护一个 enum 来表达对于 0、1 这样的数据值正确的理解
基于这样重复劳动的问题,所以就有了本文要所讲的这个小工具
转换
进行语言的转换,必须理解源语言所表达的含义,这里也就用到了传统的编译相关的一些技术
分词
分词就是将一句话分成一个个单词
如对应英文 how are you? 我们可以拆分成 how are you ?
对于程序化语言也是类似的,对于上面的 Golang 的 TaskStatus 表达式,我们最终会生成如下的 token 数据结构
[]token.Token{
{
Value: "package",
Type: "Package",
Start: {0, 0},
End: {0, 6},
},
{
Value: "task",
Type: "Identifier",
Start: {0, 8},
End: {0, 11},
},
{
Value: "type",
Type: "Type",
Start: {2, 0},
End: {2, 3},
},
{
Value: "TaskStatus",
Type: "Identifier",
Start: {2, 5},
End: {2, 14},
},
{
Value: "int",
Type: "IntType",
Start: {2, 16},
End: {2, 18},
},
{
Value: "const",
Type: "Const",
Start: {4, 0},
End: {4, 5},
},
{
Value: "(",
Type: "LeftParentheses",
Start: {4, 6},
End: {4, 6},
},
{
Value: "Todo",
Type: "Identifier",
Start: {5, 1},
End: {5, 4},
},
{
Value: "TaskStatus",
Type: "Identifier",
Start: {5, 6},
End: {5, 15},
},
{
Value: "=",
Type: "Assignment",
Start: {5, 17},
End: {5, 17},
},
{
Value: "iota",
Type: "IOTA",
Start: {5, 19},
End: {5, 22},
},
{
Value: "Pending",
Type: "Identifier",
Start: {6, 1},
End: {6, 7},
},
{
Value: "Done",
Type: "Identifier",
Start: {7, 1},
End: {7, 4},
},
{
Value: ")",
Type: "RightParentheses",
Start: {8, 0},
End: {8, 0},
},
}
分词的 workflow 如下
生成 AST
根据上一步分析出来的 token,生成对应的抽象语法树
ast.File{
Name: "task",
Body: {
ast.TypeDeclaration{
Id: "TaskStatus",
Kind: "int",
},
ast.ConstDeclaration{
BaseDeclaration: ast.BaseDeclaration{},
Declarators: {
{
Kind: "TaskStatus",
Id: "Todo",
Value: "iota",
},
{
Kind: "",
Id: "Pending",
Value: "",
},
{
Kind: "",
Id: "Done",
Value: "",
},
},
},
},
}
token 本身是上下文无关的,AST 是对于 token 的理解,组合成一个含有语法信息的数据结构,如一步我们分离出的 type Status int 三个 token,在 AST 中表示为 ****TypeDeclaration
func (a *AstGenerator) readTypeDeclaration() TypeDeclaration {
d := TypeDeclaration{}
next, _ := a.nextToken(true)
d.Id = next.Value
next, _ = a.nextToken(true)
if next.Type == token.IntType {
d.Kind = Int
} else {
d.Kind = String
}
return d
}
执行 AST
计算 enum 值
遍历上一步生成的 AST,生成相关的计算值
{
TaskStatus: {
Todo: 0,
Pending: 1,
Done: 2
}
}
根据 enum 值 生成 TS
因为只针对 enum 类型,比较简单,所以我们无需把 Golang AST 转换成 Typescript AST 进行处理,直接根据当前计算的 enum 值生成即可
namespace task {
export enum TaskStatus {
Done = 2,
Pending = 1,
Todo = 0,
}
}
Playground
地址:golang-enum-to-ts-playground.vercel.app/
因为转换的源代码是用 Golang 写的,所以这里是将 Golang 编译成 wasm 运行在浏览器的