分享

Shopee出品的 Protobuf 转换 TypeScript神器

 西北望msm66g9f 2020-05-06

作者:项伟平。Shopee金融团队架构师,负责Shopee商家侧平台的React Native项目研发的架构设计及前沿技术的探索。

源码github地址在此,记得点星:https://github.com/brandonxiang/pb-to-typescript playground页面欢迎试玩:https://brandonxiang./pb-to-typescript/example/

Why

《深入 ProtoBuf - 简介》一文介绍了Protobuf是一种有别于JSON 或 XML文本传输的新型二进制数据格式,具有安全性好,传输效率高的特点。

Protobuf文件(简称pb文件)在go语言的微服务中被广泛使用,实际开发的情景中,后端同事会丢给我们一个pb文件,也就是接口定义的参数,让前端同事按照结构体传参。这份pb往往成为前后端沟通的桥梁,所以是非常重要的。

定义一个最简单的结构体,通常如下,message 关键字后跟上消息名称,结构体内部则是字段和其数据类型。

message student { string name = 1; int32 age=2}

作为前端,你可能会想起js对象或者typescript的类型定义文件。ts类型定义文件在接口联调的过程中能够提高开发效率,有效缩减联调时间,提高代码质量。回想一下,你是不是有抱怨过后端给你的字段,在没有数据的时候,有可能是undefined,null,空数组,空字符串,这些都不算恶劣,还有'null'的字符串。这样直接让你的代码里面充满了各种奇怪的判断逻辑,这种“脏代码”直接降低你的代码可读性,严重的还会带给你bug。

var student = {  name: 'Peter',  age: 18}
interface student { name: string; age: number;}

这时候聪明的同学就会想到,能不能让protobuf直接转换成为typescript的类型定义文件。前端同事在联调的时候直接对请求函数进行类型限制,也可以根据请求返回参数的数据结构mock假数据。这种想法直接形成了有效的工作流程,给页面交互联调提高效率。

How

怎么去实现protobuf到typescript的转换,首先受到开源工具geotho/protobuf-to-typescript的启发,但是它的源码非常简单,原理不过是字符串替换,非常容易出现报错的情况。所以我需要一个标准的解释器能够精确将protobuf文件转化为抽象语法树(AST)。这时候,我阅读了@grpc/proto-loader的源码,它依赖于开源库protobuf.js。但是protobuf.js所提供的命令行工具(命令如下)不能满足我们的需求,它的转换结果更多是服务于nodejs,应用于服务端工作。

pbjs -t static-module file1.proto file2.proto | pbts -o bundle.d.ts -

现在我们的目标很明确,如何将protobuf的内容转换为我们想要的typescript类型文件。首先,引入protobufjs,使用 protobuf.parse(source, { keepCase: true })将pb进行解析(keepCase是保护参数大小写的选项),得到的是官方提供的protobuf的js对象,可以直接针对它进行解析,或者将它进行json序列化toJSON()

Type (T)ExtendsType-specific properties
ReflectionObject
options
NamespaceReflectionObjectnested
RootNamespacenested
TypeNamespacefields
EnumReflectionObjectvalues
FieldReflectionObjectrule, typeid
MapFieldFieldkeyType
OneOfReflectionObjectoneof (array of field names)
ServiceNamespacemethods
MethodReflectionObjecttype, requestTyperesponseType, requestStream, responseStream

以上是protobuf.js解析出来的数据类型,因为目标主要是面向前端使用的typescript文件,所以我们主要需要针对FieldEnumMethod这三种数据类型进行typescript转换。

Field

Field主要指的就是protobuf中的message,它可以转换为ts里面的interface。其中PB类型和TS类型存在一个转换关系。这里存在一个问题即是int64超出js的number类型,如果强制转换则会丢失精度,默认我们将它转换为string。

repeated关键字则是数组的一个表现。

Field typeExpected JS type (create, encode)
s-/u-/int32 s-/fixed32number
s-/u-/int64 s-/fixed64number或者string
float doublenumber
boolboolean
stringstring
bytesstring
Enum

枚举是Protobuf类型也是Typescript类型,只需做简单转换即可,但是Protobuf中的Enum有可能存在于message当中,而此时,它并非接口参数实现,需要注意。

Method

rpc服务里面的一个方法即是一个method,一般里面包含两个message,分别是入参和出参。

syntax = 'proto3'; service MyService { rpc MyMethod (MyRequest) returns (MyResponse); } message MyRequest { string path = 1; } message MyResponse { int32 status = 1; }

同样,我们需要把Method转换为typescript中的interface,如下,这是因为请求一般返回的是Promise对象。

interface MyMethod {  (params: MyRequest): Promise<MyResponse>;}

类型文件可以应用于你自行封装的request方法,此时代码的入参出参则被有效限制。

const myMethod: MyMethod = (params) => { return request('/my_method', params)}

通过以上说的这些转换,已经可以满足对请求函数的入参出参的typescript类型限制,有效地提高和后台开发这联调的效率。

What

功能实现后,可能有人向我推荐easy-mock。首先,easy-mock暂时没支持protobuf。它更多是一个大而全的系统,功能很齐全,但是同样也带来一些问题----接口维护成本。“mock平台需要谁来维护?”这个问题成为最大的阻碍。接口参数应该是由后端工程师定义,但是他们改了参数往往忘了维护在mock平台上。如果交给前端维护,有时候难免会导致通知不到位,沟通成本上升的情况。因此,我更崇尚于“小而美”的工具。

pb-to-typescript它即可以是运行在nodejs上,也可以跑在浏览器上。更简单的是你可以直接打开页面https://brandonxiang./pb-to-typescript/example,直接将protobuf文件复制进行转换,右方输出的则是类型定义文件。这里可以输出d.ts或typescript文件。

d.ts是类型文件,它的优点在于自动引入。但是重复的名称会带来变量污染,生产的类型文件是没法管控重名的情况,开发者需要自行增加namespace。我个人更推荐ts文件,通过importexport解决作用域的问题。

Mock

有同学肯定还是会烦恼于mock的问题。正因为我们是从“小而美”的角度触发,我们只需要mock返回参数即可。

在example页面中同样提供了这个功能,将proto文件整个复制进去,点击需要mock的方法名,mock的结果会根据typescript的类型进行简单的mock处理,基本满足页面UI的编写。以上面栗子为例,mock数据可以直接通过Promise.resolve填入request方法中,这样保证了类型检查通过,而且返回了一个标准的Promise对象。

const myMethod: MyMethod = (params) => {  //return request('/my_method', params)  return Promise.resolve({    'status': 10  });}

Conclusion

pb-to-typescript解决的问题是接口联调中的一个痛点,就是接口还没好,后端同事只提供你一个proto文件。前端同事可以通过proto的内容转换为ts的类型定义并简单mock返回数据,将UI和交互工作前置,提高工作效率,早点下班。

祝大家新的一年不用加班。

题外话

Shopee,又称虾皮,是一家腾讯投资的跨境电商平台。这里鼓励提高效率而非加班,技术氛围好。如果想和我并肩作战一起学习,可以找我内推。邮箱

weiping.xiang@shopee.com,非诚勿扰。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多