ts-proto
將您的.proto
文件轉換為強大的慣用性打字條文件!
TS-Proto的2.x釋放遷移了低級原始序列化,其encode
和decode
方法從尊貴的,但老化和停滯的protobufjs
軟件包使用到@bufbuild/protobuf
。
如果僅使用encode
和decode
方法,則應該在很大程度上是一個非破壞的更改。
但是,如果您使用了使用舊protobufjs
Writer
或Reader
類的任何代碼,則需要更新代碼以使用新的@bufbuild/protobuf
類:
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
如果遷移到@bufbuild/protobuf
是您的阻滯劑,則可以將ts-proto
版本固定在1.x
上。
免責聲明和道歉:我打算將TS-Proto 2.x發佈為Alpha版本,但沒有使語義釋放配置正確,因此TS-Proto 2.x作為主要版本發行,沒有適當的Alpha /β週期。
如果您可以在發行時遇到的任何問題提交報告(或更好的PR!),這將不勝感激。
遷移中其他人的任何提示或技巧也將不勝感激!
TS-Proto
目錄
概述
Quickstart
buf
ESM
目標
非目標
示例類型
亮點
自動批量 / n+1預防
用法
支持的選項
Nestjs支持
觀看模式
基本的GRPC實現
贊助商
發展
假設
托多
一個處理
默認值和未設置字段
眾所周知的類型
包裝器類型
JSON類型(結構類型)
時間戳
數字類型
可選值的當前狀態
TS-Proto從Protobuf模式生成打字稿類型。
IE給出了一個person.proto
。
消息人{字符串名稱= 1; }
ts-proto將生成一個person.ts
文件,例如:
接口人{ 名稱:字符串} const person = { encode(person):作者{...} 解碼(讀者):人{...} tojson(人):未知{...} fromjson(數據):person {...}}
它還知道服務,並將為他們生成類型,即:
導出接口Pingservice { ping(請求:pingrequest):Promise <PingResponse>;}
它還將生成PingService
的客戶實現;當前支持TWIRP,GRPC-WEB,GRPC-JS和NESTJS。
npm install ts-proto
protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=. ./simple.proto
(請注意,輸出參數名稱ts_proto_out
是基於插件名稱的後綴命名的,即--plugin=./node_modules/.bin/protoc-gen-ts_proto
參數,即_out
根據protoc
的CLI慣例。)
在Windows上,使用protoc --plugin=protoc-gen-ts_proto=".node_modules.binprotoc-gen-ts_proto.cmd" --ts_proto_out=. ./simple.proto
(請參閱#93)
確保您使用的是現代的protoc
(請參閱平台的安裝說明,IE protoc
V 3.0.0
不支持_opt
標誌
這將生成給定*.proto
類型的*.ts
源文件。
如果要將這些源文件包裝到NPM軟件包中以分配給客戶端,只需像往常一樣在它們上運行tsc
以生成.js
/ .d.ts
文件,然後將輸出部署為常規的NPM軟件包。
如果您使用的是BUF,請通過strategy: all
buf.gen.yaml
文件(文檔)中的所有策略。
版本:v1plugins: - 名稱:tsout:.. ../gen/tsstrategy:allpath:../ node_modules/ts-proto/protoc-gen-ts_proto
為了防止buf push
讀取無關的.proto
文件,請像這樣配置buf.yaml
:
構建:排除:[node_modules]
您也可以使用發布給BUF註冊表的官方插件。
版本:v1plugins: - 插件:buf.build/community/stephenh-ts-protoout:../ gen/tsopt: - 輸出服務= ... - useExactTypes = ...
如果您使用具有esModuleInterop
的現代TS設置或在ESM環境中運行,則需要通過以下方式傳遞ts_proto_opt
s:
esModuleInterop
esModuleInterop=true
tsconfig.json
importSuffix=.js
如果在ESM環境中執行生成的TS-Proto代碼
就ts-proto
生成的代碼而言,一般目標是:
慣用的打字稿/ES6類型
ts-proto
是內置的Google/Java式JS protoc
代碼或“ Make .d.ts
文件*.js
註釋” protobufjs
的方法
(從技術上講, protobufjs/minimal
包用於實際閱讀/寫作字節。)
打字稿優先輸出
界面上的類
盡可能多的類型只是接口,因此您可以像常規哈希/數據結構一樣使用消息。
僅支持Codegen *.proto
-to- *.ts
工作流,當前沒有動態.proto
文件的運行時反射/加載
請注意,TS-Proto不是開箱即用的RPC框架;取而代之的是,它更像是瑞士軍刀(由其許多配置選項見證),它使您可以精確地構建自己想要的RPC框架(即最好與您公司的Protobuf生態系統融合在一起;對於更好或更糟的是,Protobuf RPC仍然是一個有點分散的生態系統)。
如果您想要在TS-Proto頂部建造的開箱即用的RPC框架,則有一些示例:
不錯的GRPC
Starpc
(有關潛在貢獻者的注意,如果您開發了使用ts-proto
其他框架/迷你框架,甚至是博客文章/教程,我們很樂意鏈接到它們。)
我們也不支持google.api.http
的Google Cloud API的客戶端,如果您想提交PR,請參見#948。
生成的類型是“僅數據”,即:
導出接口簡單{ 名稱:字符串; 年齡:數字; 創建:日期|不明確的; 孩子:孩子|不明確的; 狀態:狀態; 孫子:孩子[]; 硬幣:數字[];}
以及encode
/ decode
工廠方法:
導出const simple = { create(baseObject?:deeppartial <simple>):簡單{...}, encode(消息:簡單,作者:writer = writer.create()):writer {...}, 解碼(閱讀器:讀者,長度?:編號):簡單{...}, fromjson(對象:any):簡單{...}, 源自分(object:deeppartial <simple>):simple {...}, tojson(消息:簡單):未知{...},};
這允許使用慣用的TS/JS使用:
const bytes = simple.encode({name:...,age:...,...})。完成(); const simple = simple.decode(reader.create(bytes)); const {name,年齡} =簡單;
在不創建類並調用正確的Getters/Setter的情況下轉換/從其他層轉換時,可以極大地簡化集成。
一個窮人的嘗試“請給我們可選類型”
規範的Protobuf包裝器類型,即google.protobuf.StringValue
,映射為可選值,即string | undefined
,這意味著我們可以假裝Protobuf類型系統具有可選類型。
(更新:TS-Proto現在也支持proto3 optional
關鍵字。)
時間戳被映射為Date
(可與useDate
參數配置。)
與protobufjs
不同, fromJSON
/ toJSON
使用Proto3典型的JSON編碼格式(例如時間戳為ISO字符串)。
可以將objectids映射為mongodb.ObjectId
(可與useMongoObjectId
參數配置。)
(注意:目前僅由Twirp客戶支持。)
如果您使用TS-Proto的客戶端來調用後端微服務,類似於SQL應用程序中的n+1問題,那麼微服務客戶端很容易(在服務單個請求時)無意中觸發多個單獨的RPC呼叫“獲取書1”,“獲取書2”,“獲取書3”,這確實應該分為單個“獲取書籍[1、2、3]”(假設後端支持面向批處理的RPC方法)。
TS-Proto可以為此提供幫助,從本質上可以自動將您的個人“獲取書籍”撥打到批處理“獲取書籍”電話中。
為了使TS-Proto這樣做,您需要使用以下批處理約定實現服務的RPC方法。
Batch<OperationName>
Batch<OperationName>
輸入類型具有單個重複字段(即repeated string ids = 1
)
Batch<OperationName>
輸出類型具有:
單個重複字段(即repeated Foo foos = 1
) ,其中輸出順序與輸入ids
順序相同或
輸入的映射到輸出(IE map<string, Entity> entities = 1;
)
當TS-Proto識別此模式的方法時,它將自動為客戶端創建<OperationName>
的“非批量”版本,即client.Get<OperationName>
,該版本會採用單個ID並返回單個結果。
這為客戶端代碼提供了這樣的幻覺,即它可以使個人Get<OperationName>
呼叫(在實現客戶端的業務邏輯時通常更可取/更容易/更容易),但是TS-Proto提供的實際實現最終將成為Batch<OperationName>
致電後端服務。
您還需要啟用useContext=true
構建時間參數,該參數為所有客戶端方法提供了一個go style ctx
參數,並使用getDataLoaders
方法,該方法使ts-proto cache/resolve cache/nesove request request request scoped dataLoaders提供基本的自動批次加載檢測/沖洗行為。
有關示例/更多詳細信息,請參見batching.proto
文件和相關測試。
但是淨效應是,TS-Proto可以為客戶呼叫提供SQL- / ORM風格的n+1預防,這在大容量 /高度並行實現(如GraphQl前端網關)中尤其是至關重要的。 。
ts-proto
是一個protoc
插件,因此您可以通過它運行(直接在您的項目中,或者更有可能在單聲道架構管道中,即像Ibotta一樣,即):
將ts-proto
添加到您的package.json
運行npm install
以下載
用plugin
參數調用protoc
:
protoc -plugin = node_modules/ts-proto/protoc-gen-ts_proto ./batching.proto -i。
也可以使用Protobuf-Gradle-Plugin與Gradle一起調用ts-proto
:
Protobuf { 插件{//可以用任何未使用的插件名稱代替,例如tsproto`ts { 路徑='路徑/到/插件'} } //僅當您提供插件optiongeneratePrototasks { all()。每個{task-> task.plugins {//必須匹配插件ID為abovets { 選項'foo = bar'} } } } }
生成的代碼將放置在Gradle Build目錄中。
使用--ts_proto_opt=globalThisPolyfill=true
,TS-Proto將包含一個全局thisthis的polyfill。
默認為false
,即我們假設globalThis
可用。
使用--ts_proto_opt=context=true
,該服務將具有GO風格的ctx
參數,該參數可用於跟踪/登錄/etc。如果由於性能原因,您不使用Node的async_hooks
API。
使用--ts_proto_opt=forceLong=long
,所有64位編號將作為Long
的實例解析(使用長庫)。
使用--ts_proto_opt=forceLong=string
,所有64位的數字將作為字符串輸出。
使用--ts_proto_opt=forceLong=bigint
,所有64位編號將輸出為BigInt
s。此選項仍然使用long
庫在protobuf.js
內部內部進行編碼/解碼,但隨後在TS-Proto生成的代碼中轉換為/從BigInt
S轉換。
默認行為是forceLong=number
,它將在內部使用long
庫來編碼/解碼值(因此您仍然會在輸出中看到一個util.Long = Long
Line),但會將long
值轉換為number
自動為您。請注意,如果執行此轉換時,將拋出運行時錯誤,而64位值則比正確存儲的number
要大。
使用--ts_proto_opt=useJsTypeOverride
,將輸出64位的數字作為fieldOption.jStype在字段上指定。這比提供的forceLong
選項優先。
使用--ts_proto_opt=esModuleInterop=true
Change輸出符合esModuleInterop
。
具體來說, Long
導入將作為import Long from 'long'
而不是import * as Long from 'long'
的長期導入。
使用--ts_proto_opt=env=node
或browser
或both
兼而有之,TS-Proto將在輸出中做出特定於環境的假設。這兩者都默認為both
,這沒有特定於環境的假設。
使用node
將bytes
的類型從Uint8Array
更改為Buffer
,以便於通常使用Buffer
節點生態系統集成。
目前, browser
除了“不是node
”之外沒有任何特定的行為。它可能很快/在某個時候。
帶有--ts_proto_opt=useOptionals=messages
(用於消息字段)或--ts_proto_opt=useOptionals=all
(用於消息和標量字段),將字段聲明為可選鍵,例如, field?: Message
而不是默認field: Message | undefined
。
ts-proto默認為useOptionals=none
因為它:
為了預防錯字,可選字段使額外的字段輕鬆進入消息(直到我們獲得確切類型),即:
界面somemessage { firstName:string; lastName:string;} //用typoconst data = {firstName:“ a”,lastTypo:“ b”}; // with useOptionals = none聲明,這是正確的編譯;如果“ lastName”是可選的,則不會發出消息:somemessage = {... data};
對於一致的API,如果SomeMessage.lastName
是可選的lastName?
,然後讀者必須檢查兩個空的條件:a)是lastName
undefined
(b/c,它是在內存中創建的,並保持不設置),或者b)是lastName
空字符串(b/ c,我們在電線上讀了SomeMessage
, proto3規格,初始化的lastName
到空字符串)?
為了確保適當的初始化,如果以後的SomeMessage.middleInitial
添加,但它被標記為可選的middleInitial?
,您可能有許多在生產代碼中的呼叫站點,這些呼叫網站現在應該通過middleInitial
來創建有效的SomeMessage
,但事實並非如此。
因此,在錯覺,讀取器的矛盾和正確初始化之間,TS-Proto建議使用useOptionals=none
作為“最安全”的選項。
綜上所述,這種方法確實需要作家/創作者設置每個字段(儘管fromPartial
和create
旨在解決這個問題),因此,如果您仍然想擁有可選的鍵,則可以設置useOptionals=messages
= Message = useOptionals=all
。
(有關useOptional
的討論,請參閱此問題。)
初始化消息時預防錯別字,並且
為讀者提供最一致的API
確保所有字段都適當初始化生產消息。
使用--ts_proto_opt=exportCommonSymbols=false
,諸如DeepPartial
和protobufPackage
之類的實用程序類型不會export
。
這應該使使用生成的輸出的創建桶導入,即import * from ./foo
import * from ./bar
。
請注意,如果您在多個*.proto
文件中使用相同的消息名稱,則仍然會得到導入衝突。
使用--ts_proto_opt=oneof=unions
,將生成oneof
字段作為ADT。
請參閱“單一處理”部分。
帶有--ts_proto_opt=unrecognizedEnumName=<NAME>
枚舉將包含一個鍵<NAME>
,其中包含一個unrecognizedEnumValue
選項的鍵。
默認值為UNRECOGNIZED
。
使用--ts_proto_opt=unrecognizedEnumValue=<NUMBER>
<數字>枚舉將包含一個由unrecognizedEnumName
選項提供的鍵,值為<NUMBER>
。
默認為-1
。
使用--ts_proto_opt=unrecognizedEnum=false
Enums將不包含未識別的枚舉鍵和值,而未unrecognizedEnumName
和unrecognizedEnumValue
選項。
使用--ts_proto_opt=removeEnumPrefix=true
生成的枚舉將從成員中刪除枚舉名稱。
FooBar.FOO_BAR_BAZ = "FOO_BAR_BAZ"
將生成FooBar.BAZ = "FOO_BAR_BAZ"
使用--ts_proto_opt=lowerCaseServiceMethods=true
,服務方法的方法名稱將被降低/駱駝庫,即service.findFoo
而不是service.FindFoo
。
使用--ts_proto_opt=snakeToCamel=false
,將在消息鍵和toJSON
/ fromJSON
方法中保留字段。
snakeToCamel
也可以將其設置為_
限制的字符串列表(逗號保留為標誌界限),即--ts_proto_opt=snakeToCamel=keys_json
,其中包括keys
在其中,包括鍵在其中,將使json
keys casel casel casel be casel be casel casel casel casel casel be json keys ne json鍵是駱駝盒。
空字符串,即snakeToCamel=
,將兩個消息鍵和JSON
鍵都作為蛇形案例(與snakeToCamel=false
相同)。
請注意,要使用json_name
屬性,您必須使用json
。
默認行為是keys_json
,即兩個都將是駱駝殼,如果設置,將使用json_name
。
使用--ts_proto_opt=outputEncodeMethods=false
, Message.encode
和Message.decode
方法用於使用ProtoBuf編碼/二進制數據的方法將不會輸出。
如果您只需要“類型”,這將很有用。
使用--ts_proto_opt=outputJsonMethods=false
, Message.fromJSON
和Message.toJSON
方法,用於使用JSON編碼數據的方法將不會輸出。
如果您只需“類型”,這也很有用。
使用--ts_proto_opt=outputJsonMethods=to-only
- --ts_proto_opt=outputJsonMethods=from-only
您將只能在Message.toJSON
和Message.fromJSON
方法之間導出一個。
如果您僅使用ts-proto來encode
或decode
而不是兩者都不適用,這將很有用。
使用--ts_proto_opt=outputPartialMethods=false
, Message.fromPartial
Message.create
使用--ts_proto_opt=stringEnums=true
,生成的枚舉類型將基於字符串而不是基於INT。
如果您只需“僅類型”,並且正在使用配置以序列化枚舉為字符串的GRPC REST網關,這將很有用。
(需要outputEncodeMethods=false
。)
使用--ts_proto_opt=outputClientImpl=false
,客戶端實現,即實現客戶端端的FooServiceClientImpl
(在twirp中,請參見grpc-web
的下一個選項)RPC接口將不會輸出。
使用--ts_proto_opt=outputClientImpl=grpc-web
,客戶端實現,即FooServiceClientImpl
,將在運行時使用 @Impobable-eng/grpc-Web庫將GRPC消息發送到GRPC-WEB Backend。
(請注意,這僅使用GRPC-WEB運行時,您無需使用其生成的任何代碼,即TS-Proto輸出替代其ts-protoc-gen
輸出。)
您需要添加@improbable-eng/grpc-web
,並添加到項目package.json
的運輸;有關工作示例,請參見integration/grpc-web
目錄。另請參閱#504與GRPC-Web-Devtools集成。
使用--ts_proto_opt=returnObservable=true
,服務方法的返回類型將被Observable<T>
而不是Promise<T>
。
使用--ts_proto_opt=addGrpcMetadata=true
,服務方法的最後一個參數將接受GRPC Metadata
類型,其中包含帶有呼叫的其他信息(即訪問tokens/etc。)。
(需要nestJs=true
。)
使用--ts_proto_opt=addNestjsRestParameter=true
,服務方法的最後一個參數將是帶有任何類型的REST參數。這樣,您可以使用通常在Nestjs中使用的自定義裝飾器。
(需要nestJs=true
。)
使用--ts_proto_opt=nestJs=true
,默認值將更改為生成Nestjs Protobuf友好類型和服務接口,這些類型和服務接口都可以在Nestjs Protobuf實現的客戶端和服務器端使用。有關更多信息和實施示例,請參見Nestjs Readme。
具體而言, outputEncodeMethods
, outputJsonMethods
和outputClientImpl
都是錯誤的, lowerCaseServiceMethods
將是真實的,並且將忽略outputServices
。
請注意, addGrpcMetadata
, addNestjsRestParameter
和returnObservable
仍然是錯誤的。
使用--ts_proto_opt=useDate=false
,類型google.protobuf.Timestamp
的字段不會被映射到生成類型中的類型Date
。有關更多詳細信息,請參見時間戳。
使用--ts_proto_opt=useMongoObjectId=true
,一種稱為Objectid的字段的字段,其中構造消息在字段上構造為“值為值”字符串的字符串將映射到生成類型中的mongodb.ObjectId
。這將要求您的項目安裝MongoDB NPM軟件包。有關更多詳細信息,請參見ObjectID。
使用--ts_proto_opt=annotateFilesWithVersion=false
,生成的文件將不包含用於生成文件的protoc
和ts-proto
的版本。此選項通常設置為true
,因此文件列出了使用的版本。
使用--ts_proto_opt=outputSchema=true
,將生成元鍵入,以後可以在其他代碼生成器中使用。
使用--ts_proto_opt=outputSchema=no-file-descriptor
,將生成元鍵入,但我們不在生成的模式中包含文件描述符。如果您試圖最大程度地減少生成的模式的大小,這將很有用。
使用--ts_proto_opt=outputSchema=const
,將生成Meta鍵入as const
,允許對其所有屬性的類型安全訪問。 (僅適用於Typescript 4.9及以上,因為它也使用satisfies
操作員)。可以將no-file-descriptor
選項( outputSchema=const,outputSchema=no-file-descriptor
)結合使用,以在生成的模式中包括文件描述符。
使用--ts_proto_opt=outputTypeAnnotations=true
,將給出每個消息,一個$type
字段,其中包含其完全合格的名稱。您可以使用--ts_proto_opt=outputTypeAnnotations=static-only
省略interface
聲明,或--ts_proto_opt=outputTypeAnnotations=optional
以使其成為interface
定義的可選屬性。如果您要使用$type
字段進行運行時類型檢查服務器的響應,則後一個選項可能會很有用。
使用--ts_proto_opt=outputTypeRegistry=true
,將生成類型註冊表,可用於通過完全合格的名稱來解決消息類型。另外,將為每條消息提供一個包含其完全符合條件的名稱的$type
字段。
使用--ts_proto_opt=outputServices=grpc-js
,TS-Proto將以GRPC-JS格式輸出服務定義和服務器 /客戶端存根。
使用--ts_proto_opt=outputServices=generic-definitions
,TS-Proto將輸出通用(Framework-Aggnostic)服務定義。這些定義包含每種方法的描述符,其中包含鏈接到請求和響應類型的鏈接,該鏈接允許在運行時生成服務器和客戶端存根,並在編譯時為其生成強大的類型。使用此方法的庫的一個示例是不錯的GRPC。
使用--ts_proto_opt=outputServices=nice-grpc
,TS-Proto將輸出服務器和客戶端存根,以獲取NICE GRPC。這應該與通用定義一起使用,即您應該指定兩個選項: outputServices=nice-grpc,outputServices=generic-definitions
。
使用--ts_proto_opt=metadataType=Foo@./some-file
使用--ts_proto_opt=outputServices=generic-definitions,outputServices=default
,ts-proto將輸出通用定義和接口。如果您想依靠接口,這很有用,但在運行時也具有一些反射功能。
使用--ts_proto_opt=outputServices=false
,or =none
,ts-proto將不會輸出服務定義。
使用--ts_proto_opt=rpcBeforeRequest=true
,TS-Proto將在RPC接口定義中添加功能定義,其中籤名: beforeRequest(service: string, message: string, request: <RequestType>)
。它還將自動設置outputServices=default
。每種服務的方法都將在執行其請求之前調用beforeRequest
。
使用--ts_proto_opt=rpcAfterResponse=true
,TS-Proto將在RPC接口定義中添加功能定義: afterResponse(service: string, message: string, response: <ResponseType>)
。它還將自動設置outputServices=default
。每個服務的方法都會在返迴響應之前調用afterResponse
。
使用--ts_proto_opt=rpcErrorHandler=true
,TS-Proto將在RPC接口定義中添加一個功能定義:thange error: handleError(service: string, message: string, error: Error)
。它還將自動設置outputServices=default
。
使用--ts_proto_opt=useAbortSignal=true
,生成的服務將接受AbortSignal
以取消RPC調用。
使用--ts_proto_opt=useAsyncIterable=true
,生成的服務將使用AsyncIterable
服務而不是Observable
。
帶有--ts_proto_opt=emitImportedFiles=false
,ts-proto不會發出google/protobuf/*
文件,除非您明確將文件添加到protoc
例如protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto my_message.proto google/protobuf/duration.proto
使用--ts_proto_opt=fileSuffix=<SUFFIX>
,TS-Proto將使用指定的後綴發布生成的文件。帶有fileSuffix=.pb
helloworld.proto
文件將被生成為helloworld.pb.ts
。這是其他ProtoC插件中的常見行為,並提供了一種方法來快速將所有生成的文件覆蓋。
使用--ts_proto_opt=importSuffix=<SUFFIX>
,TS-Proto將使用指定的後綴發射文件導入。用fileSuffix=.js
的helloworld.ts
導入將生成import "helloworld.js"
。默認值是在沒有文件擴展程序的情況下導入。由打字稿4.7.x及以上支持。
使用--ts_proto_opt=enumsAsLiterals=true
,生成的枚舉類型將是枚舉的對象,AS as const
。
使用--ts_proto_opt=useExactTypes=false
, fromPartial
和create
方法生成的方法將不使用精確的類型。
默認行為是useExactTypes=true
,它使得fromPartial
製成並為其參數create
精確的類型,以使打字稿拒絕任何未知屬性。
使用--ts_proto_opt=unknownFields=true
,所有未知字段將被解析並輸出為緩衝區數組。
使用--ts_proto_opt=onlyTypes=true
,只會發出類型,並且將排除long
的導入和protobufjs/minimal
導入。
這與設置outputJsonMethods=false,outputEncodeMethods=false,outputClientImpl=false,nestJs=false
使用--ts_proto_opt=usePrototypeForDefaults=true
,生成的代碼將與Object.create
一起包裝新對象。
這允許代碼進行Hazzer檢查以檢測何時應用默認值,這是由於原始3的行為不將默認值放在電線上,通常僅對與proto2消息進行交互有用。
啟用後,默認值將從原型繼承,因此代碼可以使用object.keys()。inclate(“ somefield”)來檢測某些場地是否實際上是解碼的。
請注意,如前所述,這意味著對象。鍵將不包括設置默認字段,因此,如果您具有以通用方式迭代的代碼,則它也必須迭代從原型中繼承的鍵進行迭代。
使用--ts_proto_opt=useJsonName=true
,將使用Protofiles中定義的json_name
代替消息字段名稱。
使用--ts_proto_opt=useJsonWireFormat=true
,生成的代碼將反映Protobuf消息的JSON表示形式。
需要只需onlyTypes=true
。 inmans useDate=string
and stringEnums=true
。此選項是生成可以直接用於編組/刪除序列化為JSON的Protobuf消息的類型。您可能還需要設置useOptionals=all
,因為GRPC網關不需要為標量值發送默認值。
使用--ts_proto_opt=useNumericEnumForJson=true
,JSON Converter( toJSON
)將編碼ENUM值為INT,而不是字符串字面。
使用--ts_proto_opt=initializeFieldsAsUndefined=false
,將從生成的基本實例中省略所有可選字段初始化器。
使用--ts_proto_opt=disableProto2Optionals=true
,proto2文件上的所有可選字段都不會設置為可選。請注意,此標誌主要是為了保存TS-Proto對Proto2文件的遺產處理,以避免破壞更改,因此,它不打算向前推進。
使用--ts_proto_opt=disableProto2DefaultValues=true
,proto2文件中指定默認值的所有字段實際上不會使用該默認值。請注意,此標誌主要是為了保存TS-Proto對Proto2文件的遺產處理,以避免破壞更改,因此,它不打算向前推進。
with --ts_proto_opt=Mgoogle/protobuf/empty.proto=./google3/protobuf/empty
,('m'含義'importmapping',類似於protoc-gen-go),生成的代碼導入for ./google/protobuf/empty.ts
將反映覆蓋價值:
Mfoo/bar.proto=@myorg/some-lib
將映射foo/bar.proto
導入到import ... from '@myorg/some-lib'
。
Mfoo/bar.proto=./some/local/lib
將映射foo/bar.proto
導入到import ... from './some/local/lib'
。
Mfoo/bar.proto=some-modules/some-lib
將映射foo/bar.proto
導入到import ... from 'some-module/some-lib'
注意:用途是累積的,因此以--ts_proto_opt=M... --ts_proto_opt=M...
ts_proto_opt
形式期望多個值。
注意:將不會生成匹配映射導入的原始文件。
使用--ts_proto_opt=useMapType=true
,Protobuf map<key_type, value_type>
的生成代碼將成為Map<key_type, value_type>
使用Javascript Map類型。
默認行為是useMapType=false
,它使其生成了Protobuf映射的代碼map<key_type, value_type
帶有key-value對,例如{[key: key_type]: value_type}
。
使用--ts_proto_opt=useReadonlyTypes=true
,使用Typescript的readonly
修改器將生成的類型聲明為不可變。
使用--ts_proto_opt=useSnakeTypeName=false
將從類型中刪除蛇殼。
示例Protobuf
消息框{消息元素{消息圖像{enum Arignment {left = 1; 中心= 2; 正確= 3; } } } }
默認情況下,這將啟用這將生成Box_Element_Image_Alignment
的類型。通過禁用此選項,生成的類型將是BoxElementImageAlignment
。
使用--ts_proto_opt=outputExtensions=true
,生成的代碼將包括proto2擴展名
擴展編碼/解碼方法符合outputEncodeMethods
選項,如果unknownFields=true
,則將為可擴展消息創建setExtension
和getExtension
方法,也符合outputEncodeMethods
(setExtension = encode = encode,getextension,getextension = Decode)。
使用--ts_proto_opt=outputIndex=true
,將基於原始軟件包名稱空間生成索引文件。
這將禁用exportCommonSymbols
以避免在公共符號上碰撞。
使用--ts_proto_opt=emitDefaultValues=json-methods
,生成的TOJSON方法將發射諸如0
和""
JSON字段之類的標量。
使用--ts_proto_opt=comments=false
,不會將註釋從原始文件複製到生成的代碼。
使用--ts_proto_opt=bigIntLiteral=false
,生成的代碼將使用BigInt("0")
而不是0n
用於Bigint文字。當“目標”編譯器選項設置為比“ ES2020”年齡的事物時,BigInt文字不受打字稿的支持。
使用--ts_proto_opt=useNullAsOptional=true
, undefined
值將轉換為null
,如果您在.proto
文件中使用optional
標籤,則該字段也將具有undefined
類型。例如:
使用--ts_proto_opt=typePrefix=MyPrefix
,生成的接口,枚舉和工廠將在其名稱中具有MyPrefix
的前綴。
使用--ts_proto_opt=typeSuffix=MySuffix
,生成的接口,枚舉和工廠將以其名稱中的MySuffix
後綴。
Message Profileinfo {Int32 ID = 1; String Bio = 2; String Phone = 3; }消息部{int32 ID = 1;字符串名稱= 2; }消息用戶{int32 ID = 1;字符串用戶名= 2;/* ProfileInfo將在Typescript中是可選的,該類型將為ProfileInfo | null |在您不想為配置文件提供任何價值的情況下,這是不確定的。 */可選的profileinfo profile = 3;/*部門僅接受部門類型或null,因此這意味著如果沒有可用的值,則必須將其傳遞為null。 */部門= 4; }
生成的接口將是:
導出接口profileinfo { id:數字; 生物:字符串; 電話:字符串;}導出接口部門{ id:數字; 名稱:字符串;}導出接口用戶{ id:數字; 用戶名:字符串; 個人資料?:ProfileInfo | null |不明確的; //檢查這個 部門:部門|無效的; //檢查這個}
使用--ts_proto_opt=noDefaultsForOptionals=true
,根據Protobuf Spec不會默認undefined
原始值。另外,與標準行為不同,當將字段設置為其標準默認值時,它將被編碼,允許將其通過電線發送並與未定義值區分開。例如,如果一條消息沒有設置布爾值,則通常將其默認為false
,這與未定義不同。
此選項允許庫與Square/Block維護和使用的導線實現以兼容的方式行動。注意:此選項僅與使用電線或TS-Proto生成的其他客戶端/服務器代碼結合使用,並啟用了此選項。
我們有一種與Nestjs一起工作的好方法。 ts-proto
為您的控制器,客戶端生成interfaces
和decorators
。有關更多信息,請參見Nestjs Readme。
如果您想在原始文件的每一個更改上運行ts-proto
,則需要使用Chokidar-Cli之類的工具,並將其用作package.json
中的腳本。
“原始:生成”:“ Protoc -ts_proto_out = ../< Proto_path>/< Proto_name> .proto -ts_proto_opt = esmoduleopt = esmoduleop = true”,“ proto:proto:watch” proto:watch“:” chokidar“ chokidar” **/*。 C“ NPM運行原始:生成”
ts-proto
是RPC Framework不可知論 - 您如何向數據源傳輸數據取決於您。生成的客戶端實現都期望rpc
參數,哪種類型是這樣定義的:
接口RPC { 請求(服務:字符串,方法:字符串,數據:uint8array):Promise <uint8array>;}
如果您正在與GRPC合作,那麼一個簡單的實現可能會這樣:
const conn = new grpc.client( “ localhost:8765”, grpc.credentials.createinsecure()); type rpcimpl =(服務:字符串,方法:字符串,數據:uint8array)=> Promise <Uint8Array>; const sendrequest:rpcimpl =(rpcimpl =(service,service,method,data )=> {=> { //傳統上,在GRPC中,請求路徑看起來像 //“ package.names.names.serviceName/methodName”, //因此,我們構建了這樣的字符串 const路徑=`/$ {service}/$ {method}`; 返回新的承諾(((解決,拒絕)=> {// makeunaryRequest用回調傳輸結果(和錯誤)//將其轉換為Promise!const resultCallback:unaryCallback <any> =(err,res,res)=> {如果(err){return reffec(err);} resolve(res);};函數passthrough(參數:any){return groment;} //使用PassIrthough作為序列化和deperialialize functionsconn.makeunaryrequest(path,passthrough,passthrough ,passthrough,passthrough,passthrough,data,data ,ResultCallback); });}; const rpc:rpc = {request:sendrequest};
感謝我們的讚助商:
Ngrok資助了TS-Proto的最初GRPC-WEB支持。
如果您需要對公司的TS-Proto自定義或優先支持,則可以通過電子郵件發送我。
本節介紹瞭如何直接對TS-Proto做出貢獻,即在protoc
中運行ts-proto
或使用生成的打字稿並不是必需的。
要求
Docker
yarn
- npm install -g yarn
設定
下面的命令假設您已安裝了Docker 。如果您使用的是OS X,請安裝Coreutils , brew install coreutils
。
查看存儲庫中的最新代碼。
運行yarn install
以安裝依賴項。
運行yarn build:test
以生成測試文件。
這運行以下命令:
proto2ts
- 在integration/**/*.proto
文件上運行ts-proto
以生成.ts
文件。
proto2pbjs
- 使用pbjs
生成參考實現,以測試兼容性。
運行yarn test
工作流程
為您的用例添加/更新集成測試
您也可以讓yarn watch
運行,它應該“做正確的事”
與必要的ts_proto_opt
參數進行新的integration/your-new-test/parameters.txt
創建最小的integration/your-new-test/your-new-test.proto
要么找到一個現有的integration/*
測試與您的用例足夠接近,例如,EG具有一個parameters.txt
,該參數匹配ts_proto_opt
參數以重現您的用例
如果創建新的集成測試:
對your-new-test.proto
或現有integration/*.proto
文件進行任何更改後,運行yarn proto2bin
添加/更新一個integration/your-new-test/some-test.ts
單元測試,即使它與僅確保生成的代碼編譯一樣微不足道
修改ts-proto
代碼生成邏輯:
或yarn proto2ts your-new-test
以重新計算特定測試
再次離開yarn watch
應“做正確的事”
最重要的邏輯是在src/main.ts中找到的。
對src/*.ts
文件進行了任何更改後,運行yarn proto2ts
重新計算所有集成測試
運行yarn test
以驗證您的更改通過所有現有測試
提交並提交公關
有時會皺眉,但鑑於TS-Proto的主要工作是生成代碼,看到PRS中的Codegen diffs很有幫助
運行yarn format
以格式化打字稿文件。
確保在integration/your-new-test
中git add
所有*.proto
, *.bin
和*.ts
文件
項目中的測試
只要您手動運行yarn build
,您就可以通過運行yarn add ts-proto@./path/to/ts-proto
dockerized Protoc
存儲庫包括一個dockerized版本的protoc
,該版本在Docker-compose.yml中配置。
如果您想用已知版本的protoc
手動調用插件,它可能會很有用。
用法:
#在您的外殼中包括原始別名。.Aliases.sh#照常運行原始的。 ts-proto目錄可在/ts-proto.protoc-protoc -plugin =/ts-proto/protoc-gen-ts_proto -ts_proto_out =。/output -i = ./ protos ./ protos ./protoc/* .proto.或proto#或使用為您指定插件路徑的ts-protoc別名。
所有路徑必須是主機當前工作目錄中的相對路徑。 ../
不允許
在Docker容器中,通往項目根的絕對路徑是/ts-proto
容器將當前的工作目錄安裝在/host
中,並將其設置為其工作目錄。
一旦aliases.sh
來源,您就可以在任何文件夾中使用protoc
命令。
TS/ES6模塊名稱是原始軟件包
支持基於fromJSON
toJSON
持續時間的編碼
使oneof=unions-value
2.0中的默認行為
可能在2.0中更改forceLong
默認值,應默認為forceLong=long
使esModuleInterop=true
2.0中的默認值
默認情況下,TS-Proto在消息中“平坦”模型oneof
字段,例如:
消息foo {oneof atof awifef {字符串field_a = 1;字符串field_b = 2; } }
將生成具有兩個字段的Foo
類型: field_a: string | undefined;
和field_b: string | undefined
。
使用此輸出,您必須同時檢查if object.field_a
和if object.field_b
,如果設置一個,則必須記住要揭開另一個。
相反,我們建議使用oneof=unions-value
選項,該選項將將輸出更改為代數數據類型/ADT,例如:
接口yourmessage { 要么菲爾德?:{$ case:“ field_a”;值:字符串} | {$ case:“ field_b”;值:字符串};}
因為這將自動執行一次field_a
或field_b
“被設置”的一個,因為值存儲在eitherField
字段中,該字段一次只能具有一個值。
(請注意,Protobuf中的eitherField
是可選的b/c oneof
表示“最多一個字段”,並不意味著必須設置其中一個字段。)
在TS-Proto當前未解決的2.x版本中, oneof=unions-value
將成為默認行為。
還有一個oneof=unions
選項,該選項生成一個聯合,每個選項中都包含字段名稱:
接口yourmessage { 要么菲爾德?:{$ case:“ field_a”; field_a:字符串} | {$ case:“ field_b”; field_b:string};}
不再建議這樣做,因為很難編寫代碼和類型來處理多個Oneof選項:
以下助手類型可能會使使用由oneof=unions
生成的類型更容易,儘管如果您使用oneof=unions-value
,通常不需要它們:
/**從單個字段中提取所有情況名稱。 */type Oneofcases <t> = t擴展{$ case:Cheble U擴展字符串}? u:從不;/**從單個字段*/type OneofValues <t> = t擴展{$ case:ceash u擴展字符串; [鍵:字符串]:未知}? t [u]:從不;/**根據其字段名稱*/type Oneofcase <t,k擴展Oneofcases <t >> = t擴展{ $ case:k; [鍵:字符串]:未知;} ? t :Never;/**從OneOf字段*/type OneofValue <t,k擴展Oneofcases <t >> = t擴展{ $案例:推斷u擴展k; [鍵:字符串]:未知;} ? t [u] : 絕不;
為了進行比較, oneof=unions-value
的等效物:
/**從單個字段中提取所有情況名稱。 */type Oneofcases <t> = t ['$ case'];/**從OneOf字段中提取所有值類型的結合*/type OneOfValues <t> = t ['value'];/**基於字段名稱*/type Oneofcase <t,k擴展Oneofcases <t >> = t的特定類型的特定類型 $ case:k; [鍵:字符串]:未知;} ? t :Never;/**從OneOf字段*/type OneofValue <t,k擴展Oneofcases <t >> = t擴展{ $案例:推斷u擴展k; 價值:未知;} ? t [u] : 絕不;
在Core Protobuf(以及ts-proto
)中,未設置或等於默認值的值不會通過電線發送。
例如,消息的默認值undefined
。原始類型採用其自然默認值,例如string
為''
, number
為0
,等。
Protobuf之所以選擇/執行此行為,是因為它可以使兼容性具有向前的兼容性,因為即使原始場所被過時的代理忽略,原始字段也將始終具有一個值。
這很好,但這也意味著在ts-proto
字段中無法區分默認值和未設置值。從根本上講,這是Protobuf的工作原理。
如果您需要可以檢測到設置/不設置的原始字段,請參見包裝器類型。
編碼 /解碼
ts-proto
遵循Protobuf規則,並在解碼時始終返回UNSETS字段的默認值,同時以二進制格式序列化時從輸出中省略它們。
語法=“ proto3”; message foo {字符串bar = 1; }
Protobufbytes; //假設這是一個空的foo對象,protobuf binary formatfoo.decode(protobufbytes); // => {bar:''}
foo.encode({bar:“”}); // => {},以Protobuf二進制格式寫一個空的foo對象
Fromjson / Tojson
讀取JSON還將初始化默認值。由於發件人可以省略未設置字段,也可以將其設置為默認值,請使用fromJSON
將輸入歸一化。
foo.fromjson({}); // => {bar:''} foo.fromjson({bar:“”}); // => {bar:''} foo.fromjson({bar:“ baz”}); // => {bar:'baz'}
在編寫JSON時, ts-proto
通過省略設置為默認值的未設置字段和字段來將消息歸一化。
foo.tojson({}); // => {} foo.tojson({bar:undefined}); // => {} foo.tojson({bar:“”}); // => {} - 注意:省略默認值,如Expectfoo.tojson({bar:“ baz”}); // => {bar:'baz'}
Protobuf帶有幾種預定義的消息定義,稱為“眾所周知的類型”。它們的解釋是由Protobuf規範定義的,並且庫有望將這些消息轉換為目標語言中相應的本機類型。
ts-proto
當前會自動將這些消息轉換為相應的本機類型。
google.protobuf.boolvalue⇆ boolean
Uint8Array
Google.protobuf.doublevalue⇆ number
google.protobuf.fieldmask⇆ string[]
Google.protobuf.floatvalue⇆ number
Google.protobuf.int32Value⇆ number
Google.protobuf.int64Value⇆ number
google.protobuf.listvalue⇆ any[]
Google.protobuf.uint32Value⇆ number
Google.protobuf.uint64Value⇆ number
Google.protobuf.StringValue⇆ string
google.protobuf.value⇆ any
(即number | string | boolean | null | array | object
)
google.protobuf.struct⇆ { [key: string]: any }
包裝器類型是包含單個原始字段的消息,並且可以在.proto
文件中導入import "google/protobuf/wrappers.proto"
。
由於這些是消息,因此它們的默認值undefined
,從而使您可以在使用包裝器類型時將未設置的原始值與默認值區分開。 ts-proto
以<primitive> | undefined
形式生成這些字段。 <primitive> | undefined
。
例如:
// protobufsyntax =“ proto3”; import“ google/protobuf/wrappers.proto”; message exipplemessage {google.protobuf.stringvalue name = 1; }
// typeScriptInterface extplemessage { 名稱:字符串|不明確的;}
編碼消息時,原始值將轉換回其相應的包裝器類型:
explemessage.encode({name:“ foo”}); // => {name:{value:'foo'}},在二進制中
當調用Tojson時,該值不會轉換,因為包裝器類型在JSON中是慣用的。
explemessage.tojson({name:“ foo”}); // => {name:'foo'}
Protobuf的語言和類型不足以表示所有可能的JSON值,因為JSON可能包含預先未知類型的值。因此,Protobuf提供了多種代表任意JSON值的其他類型。
這些稱為struct類型,可以在import "google/protobuf/struct.proto"
的.proto
文件中導入。
any
這是最通用的類型,可以表示任何JSON值(IE number | string | boolean | null | array | object
)。
google.protobuf.listvalue⇆ any[]
代表JSON陣列
google.protobuf.struct⇆ { [key: string]: any }
代表JSON對象
ts-proto
會自動在這些結構類型及其相應的JSON類型之間來迴轉換。
例子:
// protobufsyntax =“ proto3”; import“ google/protobuf/struct.proto”; message exipplemessage {google.protobuf.value notings = 1; }
// typeScriptInterface extplemessage { 任何東西:任何|不明確的;}
編碼嵌入消息中的JSON值,將其轉換為結構類型:
exipplemessage.encode({{nothing:{name:“ hello”}});/*輸出以下結構,以Protobuf binary格式編碼:{nothing:value {structValue = structvalue = struct {fields = [mapEntry = [key =“ name name,” value = value {stringValue =“ hello”}]}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} */
google.protobuf.Timestamp
的表示形式可通過useDate
標誌配置。當useDate
是false
時, useJsonTimestamp
標誌控制精度。
Protobuf眾所周知的類型 | 默認/ useDate=true | useDate=false | useDate=string | useDate=string-nano |
---|---|---|---|---|
google.protobuf.Timestamp | Date | { seconds: number, nanos: number } | string | string |
當使用useDate=false
和useJsonTimestamp=raw
時間戳為{ seconds: number, nanos: number }
,但具有納秒精度。
當使用useDate=string-nano
Timestamp時,將其表示為ISO字符串,具有納秒精度為1970-01-01T14:27:59.987654321Z
,並依賴於納米日期庫進行轉換。您需要將其安裝在項目中。
默認情況下,數字被假定為普通的JavaScript number
s。
對於int32
和float
等Protobuf類型來說,這是可以的,但是像int64
這樣的64位類型不能100%由JavaScript的number
類型表示,因為int64
可以比number
更大/更小的值。
TS-Proto的默認配置(即forceLong=number
)仍將number
用於64位字段,然後如果值(在運行時)大於Number.MAX_SAFE_INTEGER
。
如果您期望使用64位 /高於MAX_SAFE_INTEGER
值,則可以使用TS-Proto forceLong
選項,該選項使用長NPM軟件包來支持整個64位值的範圍。
基於forceLong
配置選項的Protobuf數字類型映射到JavaScript類型:
Protobuf數字類型 | 默認/ forceLong=number | forceLong=long | forceLong=string |
---|---|---|---|
雙倍的 | 數字 | 數字 | 數字 |
漂浮 | 數字 | 數字 | 數字 |
INT32 | 數字 | 數字 | 數字 |
INT64 | 數字* | 長的 | 細繩 |
Uint32 | 數字 | 數字 | 數字 |
Uint64 | 數字* | 未簽名長 | 細繩 |
sint32 | 數字 | 數字 | 數字 |
sint64 | 數字* | 長的 | 細繩 |
固定32 | 數字 | 數字 | 數字 |
固定64 | 數字* | 未簽名長 | 細繩 |
Sfixed32 | 數字 | 數字 | 數字 |
Sfixed64 | 數字* | 長的 | 細繩 |
其中(*)表示他們可能在運行時丟失錯誤。
必需的原始內容:使用原理,即string name = 1
。
可選的原始內容:使用包裝器類型,即StringValue name = 1
。
必需消息:不可用
可選消息:使用AS-IS,IE SubMessage message = 1
。