ts-proto
verwandelt Ihre.proto
Dateien in stark typische, idiomatische Typscript-Dateien!
Die 2.x-Freisetzung von TS-Proto migrierte die Low-Level-Protobuf-Serialisierung, die seine encode
und decode
aus dem ehrwürdigen, aber alternden und stagnierenden protobufjs
Paket zu @bufbuild/protobuf
verwendet hat.
Wenn Sie nur die encode
und decode
verwendet haben, sollte dies größtenteils eine nicht sprechende Veränderung sein.
Wenn Sie jedoch einen Code verwendet haben, der den alten protobufjs
Writer
oder Reader
verwendet hat, müssen Sie Ihren Code aktualisieren, um die neuen @bufbuild/protobuf
-Klassen zu verwenden:
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
Wenn die Migration zu @bufbuild/protobuf
ein Blocker für Sie ist, können Sie Ihre ts-proto
Version auf 1.x
anpassen.
Haftungsausschluss und Entschuldigung: Ich hatte beabsichtigt, Ts-Proto 2.x als Alpha-Veröffentlichung zu veröffentlichen, habe aber die Semantic-Release-Konfiguration nicht korrig /Beta -Zyklus.
Wenn Sie Berichte (oder bessere PRs!) Für alle Probleme einreichen könnten, die Sie während der Veröffentlichung immer noch frisch sind, wäre dies sehr geschätzt.
Alle Tipps oder Tricks für andere in der Migration wären ebenfalls geschätzt!
TS-Proto
Inhaltsverzeichnis
Überblick
QuickStart
Buf
ESM
Ziele
Nichtgänger
Beispieltypen
Highlights
Automatische Batching / N+1 Prävention
Verwendung
Unterstützte Optionen
Nestjs Unterstützung
Uhrenmodus
Grundlegende GRPC -Implementierung
Sponsoren
Entwicklung
Annahmen
Todo
Ein Handling
Standardwerte und nicht festgelegte Felder
Bekannte Typen
Wrapper -Typen
JSON -Typen (Strukturtypen)
Zeitstempel
Zahlentypen
Aktueller Status der optionalen Werte
TS-Proto generiert TypeScript-Typen aus Protobuf-Schemata.
Dh eine person.proto
gegeben.Proto Schema wie:
Nachricht Person {String name = 1; }
TS-Proto generiert eine person.ts
Datei wie:
Schnittstellenperson { Name: String} const Person = { codieren (Person): Schriftsteller {...} Decodo (Leser): Person {...} tojson (Person): Unbekannt {...} Fromjson (Daten): Person {...}}
Es weiß auch über Dienstleistungen und generiert auch Typen für sie, dh:
Exportieren der Schnittstelle PingService { Ping (Anfrage: PingRequest): Versprechen <PingResponse>;}
Es wird auch Client -Implementierungen von PingService
generieren. Derzeit werden TWIRP, GRPC-Web, GRPC-JS und NESTJS unterstützt.
npm install ts-proto
protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=. ./simple.proto
(Beachten Sie, dass der Ausgangsparameter Name ts_proto_out
basierend auf dem Suffix des Namens des Plugins in der Suffix des Plugins im Suffix in der --plugin=./node_modules/.bin/protoc-gen-ts_proto
Parameter wird zum _out
Präfix, der _out-Präfix, benannt wird. Per protoc
CLI -Konventionen.)
Verwenden Sie unter Windows protoc --plugin=protoc-gen-ts_proto=".node_modules.binprotoc-gen-ts_proto.cmd" --ts_proto_out=. ./simple.proto
(siehe #93)
Stellen Sie sicher, dass Sie ein modernes protoc
verwenden (siehe Installationsanweisungen für Ihre Plattform, dh protoc
v 3.0.0
unterstützt das _opt
-Flag nicht
Dies generiert *.ts
Quelldateien für die angegebenen *.proto
-Typen.
Wenn Sie diese Quelldateien in ein NPM -Paket verpacken möchten, um sie an Clients zu verteilen, führen Sie einfach tsc
wie gewohnt aus, um die .js
/ .d.ts
-Dateien zu generieren, und stellen Sie die Ausgabe als reguläres NPM -Paket ein.
Wenn Sie BUF verwenden, passieren Sie strategy: all
in Ihrer buf.gen.yaml
-Datei (DOCS).
Version: v1plugins: -Name: TSout: ../gen/tsstrategy: AllPath: ../node_modules/ts-proto/protoc-zen-ts_proto
Um zu verhindern, buf push
, irrelevante .proto
-Dateien zu lesen, konfigurieren Sie buf.yaml
wie SO:
Build: Ausgeschlossen: [NODE_MODULES]
Sie können auch das offizielle Plugin verwenden, das in der BUF -Registrierung veröffentlicht wurde.
Version: v1plugins: -Plugin: buf.build/community/stephenh-ts-protoout: ../gen/tsopt: - OutputServices = ... - usexactTypes = ...
Wenn Sie ein modernes TS -Setup mit esModuleInterop
oder in einer ESM -Umgebung verwenden, müssen Sie ts_proto_opt
S von: übergeben:
esModuleInterop=true
, wenn esModuleInterop
in Ihrem tsconfig.json
und
importSuffix=.js
Wenn der generierte TS-Proto-Code in einer ESM-Umgebung ausgeführt wird
In Bezug auf den Code, ts-proto
generiert, sind die allgemeinen Ziele:
Idiomatische Typscript/ES6 -Typen
ts-proto
ist eine saubere Pause entweder aus protobufjs
integrierten Google/Java-ähnlichen JS- protoc
-Code oder dem .d.ts
*.js
(Technisch gesehen wird das protobufjs/minimal
-Paket zum tatsächlichen Lesen/Schreiben von Bytes verwendet.)
Typscript-First-Ausgabe
Schnittstellen über Klassen
So viel wie möglich sind Typen nur Schnittstellen, sodass Sie mit Nachrichten wie normalen Hashes/Datenstrukturen arbeiten können.
Unterstützt nur Codegen *.proto
-to- *.ts
Workflow, derzeit keine Laufzeitreflexion/Laden von dynamischen .proto
-Dateien
Beachten Sie, dass TS-Proto kein außergewöhnliches RPC-Framework ist. Stattdessen ist es eher ein Schweizer-Army-Messer (wie die vielen Konfigurationsoptionen beobachtet), mit dem Sie genau das RPC Protobuf RPC ist immer noch ein etwas fragmentiertes Ökosystem).
Wenn Sie ein außerhalbes RPC-Framework möchten, das auf TS-Proto aufgebaut ist, gibt es einige Beispiele:
netter grpc
Starpc
(Hinweis für potenzielle Mitwirkende, wenn Sie andere Frameworks/Mini-Frameworks oder sogar Blog-Beiträge/Tutorials entwickeln, um ts-proto
zu verwenden, verlinken wir gerne auf sie.)
Wir unterstützen Clients auch nicht für google.api.http
-basierte Google Cloud -APIs, siehe #948, wenn Sie eine PR einreichen möchten.
Die generierten Typen sind "nur Daten", dh:
Schnittstelle exportieren { Name: String; Alter: Zahl; erstellt: Datum | undefiniert; Kind: Kind | undefiniert; Staat: StateNum; Enkelkinder: Kind []; Münzen: Nummer [];}
Zusammen mit den Methoden encode
/ decode
von Fabrik -Methoden:
Export const Simple = { Create (BaseObject ?: Deeppartial <fancy>): Simple {...}, Encodes (Nachricht: Einfach, Schriftsteller: writer = writer.create ()): writer {...}, Decodes (Leser: Leser, Länge ?: Nummer): Einfach {...}, Fromjson (Objekt: Any): Einfach {...}, from partial (Objekt: Deeppartial <fancy>): einfach {...}, tojson (meldung: einfach): unbekannt {...},};
Dies ermöglicht die idiomatische TS/JS -Verwendung wie:
const bytes = Simple.Codode ({Name: ..., Alter: ..., ...}). Finish (); const Simple = Simple.Decode (reader.create (bytes)); const {Name, Alter } = einfach;
Dies kann die Integration beim Konvertieren in/von anderen Ebenen dramatisch erleichtern, ohne eine Klasse zu erstellen und die richtigen Getter/Setter zu rufen.
Der Versuch eines armen Mannes, "Bitte geben Sie uns optionale Typen zurück" zurück.
Die kanonischen Protobuf -Wrapper -Typen, dh google.protobuf.StringValue
, werden als optionale Werte zugeordnet, dh string | undefined
, was bedeutet, dass Primitive wir so tun können, dass das Protobuf -System optionale Typen hat.
( UPDATE : TS-Proto unterstützt jetzt auch das optional
Proto3-Schlüsselwort.)
Zeitstempel werden als Date
zugeordnet
(Konfigurierbar mit dem useDate
.)
fromJSON
/ toJSON
verwenden das kanonische JSON -Codierungsformat von Proto3 (z. B. Zeitstempel sind ISO -Zeichenfolgen) im Gegensatz zu protobufjs
.
ObjektIDs können als mongodb.ObjectId
abgebildet werden
(Konfigurierbar mit dem useMongoObjectId
-Parameter.)
(Hinweis: Dies wird derzeit nur von den Twirp -Clients unterstützt.)
Wenn Sie die Clients von TS-Proto verwenden, um Backend Micro-Services aufzurufen, ähnlich wie das N+1-Problem in SQL-Anwendungen, kann es für Micro-Service-Clients (beim Servieren einer individuellen Anforderung) versehentlich mehrere separate RPC-Aufrufe für Micro-Service-Clients ausgelöst werden. "Get Book 1", "Get Book 2", "Get Book 3", das sollte wirklich in eine einzelne "Get Books [1, 2, 3]" angegriffen werden (vorausgesetzt, das Backend unterstützt eine batchorientierte RPC-Methode).
TS-Proto kann dabei helfen und im Wesentlichen automatisch Ihr individuelles "Get Book" -Anrufe in batchierte "Get-Bücher" -Anrufe beobachten.
Damit TS-Proto dies tun kann, müssen Sie die RPC-Methoden Ihres Dienstes mit der Batching-Konvention implementieren:
Ein Methodame von Batch<OperationName>
Der Batch<OperationName>
Eingabetyp hat ein einzelnes wiederholtes Feld (dh repeated string ids = 1
)
Der Batch<OperationName>
Ausgangstyp hat entweder a:
Ein einzelnes wiederholtes Feld (dh repeated Foo foos = 1
), wobei die Ausgangsreihenfolge mit der Reihenfolge der Eingangs ids
übereinstimmt oder
Eine Karte der Eingabe in eine Ausgabe (dh map<string, Entity> entities = 1;
)
Wenn TS-Proto Methoden dieses Musters erkennt, wird automatisch eine "Nicht-Batch" -Version von <OperationName>
für den client.Get<OperationName>
erstellt.
Dies liefert dem Client-Code die Illusion, dass individuelle Get<OperationName>
-Anrufe (was im Allgemeinen bei der Implementierung der Geschäftslogik des Kunden vorzuziehen ist). Die tatsächliche Implementierung Batch<OperationName>
die TS-Proto bietet Anrufe zum Backend -Service.
Sie müssen auch den Parameter useContext=true
Build-Time aktivieren, mit dem alle Client-Methoden einen ctx
Parameter des GO-Style mit einer getDataLoaders
-Methode ermöglicht werden Erkennung/Spülenverhalten.
Beispiele/weitere Details finden Sie in der Datei batching.proto
und zugehörigen Tests.
Der Nettoeffekt besteht jedoch darin, dass TS-Proto SQL- / ORM-N+1-Prävention für Client-Aufrufe liefern kann, was insbesondere bei hochvolumigen / hochparallelen Implementierungen wie GraphQL-Front-End-Gateways-Rufe von Backend Micro-Services von entscheidender Bedeutung sein kann .
ts-proto
ist ein protoc
Plugin.
Fügen Sie ts-proto
zu Ihrem package.json
Führen Sie npm install
aus, um es herunterzuladen
Rufen Sie protoc
mit einem plugin
-Parameter auf wie:
protoc --plugin = node_modules/ts-proto/protoc-genroto ./batching.proto -i.
ts-proto
kann auch mit Gradle unter Verwendung des Protobuf-Gradle-Plugin aufgerufen werden:
protobuf { Plugins {// `ts` kann durch einen nicht verwendeten Plugin -Namen ersetzt werden, z. B. TSPROTO`TS { path = 'path/to/plugin'} } // Dieser Abschnitt benötigt nur, wenn Sie Plugin OptionsgeneratePrototass {angeben { All (). Jede {Aufgabe -> Task.plugins {// muss mit Plugin -ID übereinstimmen, die auf {{deklariert ist { Option 'foo = bar'} } } } }
Der generierte Code wird im Gradle Build -Verzeichnis platziert.
Mit --ts_proto_opt=globalThisPolyfill=true
wird TS-Proto eine Polyfill für GlobalThis enthalten.
Standardeinstellungen zu false
, dh wir gehen davon aus, dass globalThis
ist, dass dies verfügbar ist.
Mit --ts_proto_opt=context=true
haben die Dienste einen ctx
Parameter des GO-Style, der für die Verfolgung/Protokollierung/etc. nützlich ist. Wenn Sie aus Leistungsgründen keine async_hooks
-API von Node verwenden.
Mit --ts_proto_opt=forceLong=long
, werden alle 64-Bit-Zahlen als Instanzen von Long
(unter Verwendung der langen Bibliothek) analysiert.
Mit --ts_proto_opt=forceLong=string
werden alle 64-Bit-Zahlen als Zeichenfolgen ausgegeben.
Mit --ts_proto_opt=forceLong=bigint
, werden alle 64-Bit-Zahlen als BigInt
s ausgegeben. Diese Option verwendet weiterhin die long
Bibliothek, um innerhalb von protobuf.js
intern zu codieren/zu decodieren, sondern dann in den von TS-Proto generierten Code zu/von BigInt
S konvertiert.
Das Standardverhalten ist forceLong=number
, die intern weiterhin number
long
Bibliothek long
util.Long = Long
um Werte auf dem Kabel zu codieren/zu dekodieren automatisch für Sie. Beachten Sie, dass ein Laufzeitfehler geworfen wird, wenn bei dieser Konvertierung ein 64-Bit-Wert größer ist, als korrekt als number
gespeichert werden kann.
Mit --ts_proto_opt=useJsTypeOverride
werden 64-Bit-Nummern als auf dem Feld angegebene FieldOption.jstype ausgegeben. Dies hat Vorrang vor der vorgesehenen forceLong
Option.
Mit --ts_proto_opt=esModuleInterop=true
ändert Ausgabe, um esModuleInterop
konform zu sein.
Insbesondere werden die Long
Importe import Long from 'long'
anstelle des import * as Long from 'long'
als importiert.
Mit --ts_proto_opt=env=node
oder browser
oder both
macht TS-Proto umweltspezifische Annahmen in Ihrer Ausgabe. Dies ist standardmäßig both
, was keine umweltspezifischen Annahmen ausmacht.
Die Verwendung von node
ändert die Arten von bytes
von Uint8Array
auf Buffer
um eine einfachere Integration in das Knoten -Ökosystem zu erhalten, das im Allgemeinen Buffer
verwendet.
Derzeit hat browser
kein spezifisches Verhalten als "nicht node
". Es wird wahrscheinlich bald/irgendwann.
Mit --ts_proto_opt=useOptionals=messages
(für Nachrichtenfelder) oder - -ts_proto_opt field: Message | undefined
--ts_proto_opt=useOptionals=all
(für Nachrichten- und Skalarfelder) werden Felder als optionale Schlüssel, z. B. field?: Message
field: Message | undefined
.
TS-Proto standardmäßig useOptionals=none
weil es:
Für die Vorbeugung von Tippflichtigen erleichtern optionale Felder zusätzliche Felder, in eine Nachricht zu streichen (bis wir genaue Typen erhalten), dh:
Schnittstelle Somemessage { FirstName: String; LastName: String;} // Deklared mit einem Typoconst data = {FirstName: "a", lastTypo: "b"}; // mit useoptionals = Keine, dies kann korrekt nicht kompiliert werden; Wenn `lastname` optional wäre, würde es keine Nachricht: somemessage = {... data};
Für eine konsistente API, wenn SomeMessage.lastName
optional lastName?
und dann müssen die Leser zwei leere Bedingungen überprüfen: a) ist lastName
undefined
SomeMessage
b/c Es wurde in Memory erstellt und links uneingeschränkt) oder b) lastName
leer Die Proto3 -Spezifikation hat lastName
in Leerzeichen initialisiert)?
Zur Gewährleistung einer ordnungsgemäßen Initialisierung, wenn später SomeMessage.middleInitial
hinzugefügt wird, ist jedoch als optionales middleInitial?
Möglicherweise haben Sie viele Anrufe im Produktionscode, die jetzt middleInitial
übergeben sollten, um eine gültige SomeMessage
zu erstellen, aber nicht.
Daher useOptionals=none
TS-Proto zwischen Typo-Prävention, Leserkonsistenz und ordnungsgemäßer Initialisierung.
Alles in allem erfordert dieser Ansatz, dass Autoren/Ersteller jedes Feld festlegen (obwohl fromPartial
und create
werden sollen). Wenn Sie also noch optionale Schlüssel haben möchten, können Sie useOptionals=messages
oder useOptionals=all
festlegen.
(Siehe dieses Problem und dieses Problem für Diskussionen über useOptional
.)
Verhindert Tippfehler beim Initialisieren von Nachrichten und
Bietet den Lesern die konsequenteste API
Stellt sicher, dass die Produktionsnachrichten mit allen Feldern ordnungsgemäß initialisiert werden.
Mit --ts_proto_opt=exportCommonSymbols=false
, werden Nutzentypen wie DeepPartial
und protobufPackage
nicht export
d.
Dies sollte es ermöglichen, Fassimporte der generierten Ausgabe zu erstellen, dh import * from ./foo
und import * from ./bar
.
Beachten Sie, dass Sie, wenn Sie denselben Nachrichtennamen in mehreren *.proto
-Dateien verwendet, weiterhin Importkonflikte erhalten.
Mit --ts_proto_opt=oneof=unions
werden oneof
Feldern als ADTS generiert.
Siehe den Abschnitt "Oneof Handling".
Mit --ts_proto_opt=unrecognizedEnumName=<NAME>
unrecognizedEnumValue
<NAME>
Standardmäßig UNRECOGNIZED
.
Mit --ts_proto_opt=unrecognizedEnumValue=<NUMBER>
enthält einen Schlüssel, der durch die Option unrecognizedEnumName
mit Wert von <NUMBER>
bereitgestellt wird.
Standardmäßig -1
.
Mit --ts_proto_opt=unrecognizedEnum=false
nicht unrecognizedEnumName
unrecognizedEnumValue
.
Mit --ts_proto_opt=removeEnumPrefix=true
generierte Enums haben den Enum -Namen von den Mitgliedern entfernt.
FooBar.FOO_BAR_BAZ = "FOO_BAR_BAZ"
erzeugt FooBar.BAZ = "FOO_BAR_BAZ"
Mit --ts_proto_opt=lowerCaseServiceMethods=true
werden die Methodamen von Service-Methoden gesenkt/camel-case, dh service.findFoo
anstelle von service.FindFoo
.
Mit --ts_proto_opt=snakeToCamel=false
, werden Felder sowohl in den Nachrichtenschlüsseln als auch in den toJSON
/ fromJSON
-Methoden beibehalten.
snakeToCamel
kann auch als _
-delimitierte Liste von Zeichenfolgen festgelegt werden (Komma ist als Abgrenzung des Flaggens reserviert), dh --ts_proto_opt=snakeToCamel=keys_json
, wobei einschließlich keys
Nachrichtenschlüsse zum Kamel -Fall machen und einschließlich json
-JSON -Tasten. Kamelfall.
Leere Zeichenfolge, dh snakeToCamel=
, hält beide Nachrichtenschlüsseln und JSON
-Schlüssel als Schlangenfall (es ist das gleiche wie snakeToCamel=false
).
Beachten Sie, dass Sie den json
verwenden müssen, um das Attribut json_name
zu verwenden.
Das Standardverhalten ist keys_json
, dh beides wird ein Kamelgehäuse sein, und json_name
wird verwendet, wenn festgelegt wird.
Mit --ts_proto_opt=outputEncodeMethods=false
, die Message.encode
und Message.decode
Methoden zur Arbeit mit Protobuf-codierten/binären Daten werden nicht ausgegeben.
Dies ist nützlich, wenn Sie "nur Typen" möchten.
Mit --ts_proto_opt=outputJsonMethods=false
, die Message.fromJSON
und Message.toJSON
Methoden zur Arbeit mit JSON-codierten Daten werden nicht ausgegeben.
Dies ist auch nützlich, wenn Sie "nur Typen" möchten.
Mit --ts_proto_opt=outputJsonMethods=to-only
und--ts_proto_opt Message.fromJSON
--ts_proto_opt=outputJsonMethods=from-only
können Sie nur einen zwischen der Message.toJSON
exportieren.
Dies ist nützlich, wenn Sie TS-Proto verwenden, um zu encode
oder decode
und nicht für beide.
Mit --ts_proto_opt=outputPartialMethods=false
, die Message.fromPartial
und Message.create
Methoden zum Akzeptieren teilweise geformter Objekte/Objektliterale werden nicht ausgegeben.
Mit --ts_proto_opt=stringEnums=true
, sind die generierten Enum-Typen anstelle von INT-basiert.
Dies ist nützlich, wenn Sie "nur Typen" möchten und ein GRPC -REST -Gateway verwenden, das für die Serialisierung von Enums als Zeichenfolgen konfiguriert ist.
(Benötigt outputEncodeMethods=false
.)
Mit --ts_proto_opt=outputClientImpl=false
, die Client-Implementierungen, IE FooServiceClientImpl
, die die clientseitige (in TWIRP, siehe Nächste Option für grpc-web
) RPC-Schnittstellen werden nicht ausgegeben.
Mit --ts_proto_opt=outputClientImpl=grpc-web
verwendet die Client-Implementierungen, IE FooServiceClientImpl
, die @Improbable-Eng/Grpc-Web-Bibliothek zur Laufzeit, um GRPC-Nachrichten an einen GRPC-Web-Backend zu senden.
(Beachten Sie, dass dies nur die GRPC-Web-Laufzeit verwendet. Sie müssen keinen der generierten Code verwenden, dh die TS-Proto-Ausgabe ersetzt ihre ts-protoc-gen
-Ausgabe.)
Sie müssen den @improbable-eng/grpc-web
und einen Transport zum package.json
Ihres Projekts hinzufügen. Ein funktionierendes Beispiel finden Sie im Verzeichnis integration/grpc-web
. Siehe auch #504 zur Integration in GRPC-Web-Devtools.
Mit --ts_proto_opt=returnObservable=true
ist der Rückgabetyp der Servicemethoden Observable<T>
anstelle von Promise<T>
.
Mit --ts_proto_opt=addGrpcMetadata=true
akzeptiert das letzte Argument der Servicemethoden den GRPC Metadata
-Typ, der zusätzliche Informationen mit dem Anruf enthält (dh Access Tokens/etc.).
(Erfordert nestJs=true
.)
Mit --ts_proto_opt=addNestjsRestParameter=true
ist das letzte Argument der Dienstmethoden ein REST -Parameter mit Typ Any. Auf diese Weise können Sie benutzerdefinierte Dekorateure verwenden, die Sie normalerweise in NestJs verwenden können.
(Erfordert nestJs=true
.)
Mit --ts_proto_opt=nestJs=true
ändern sich die Standardeinstellungen, um NestJs-Protobuf-freundliche Typen und Service-Schnittstellen zu generieren, die sowohl in der clientseitigen als auch in der serverseitigen Seite von NestJs-Protobuf-Implementierungen verwendet werden können. Weitere Informationen und Implementierungsbeispiele finden Sie im NestJS Readme.
Speziell outputEncodeMethods
, outputJsonMethods
und outputClientImpl
sind falsch, lowerCaseServiceMethods
sind wahr und outputServices
werden ignoriert.
Beachten Sie, dass addGrpcMetadata
, addNestjsRestParameter
und returnObservable
immer noch falsch sein.
Mit --ts_proto_opt=useDate=false
, werden Felder vom Typ google.protobuf.Timestamp
nicht so zugeordnet, dass Date
in den generierten Typen eingeteilt wird. Weitere Informationen finden Sie in TimeStamp.
Mit --ts_proto_opt=useMongoObjectId=true
, Felder eines Typs namens ObjectID, in dem die Nachricht auf dem Feld bezeichnet wird, der als Wert bezeichnet wird und in den generierten Typen auf mongodb.ObjectId
eingestuft wird. Auf diese Weise müssen Ihr Projekt das MongoDB NPM -Paket installieren. Weitere Informationen finden Sie in ObjectID.
Mit --ts_proto_opt=annotateFilesWithVersion=false
enthalten die generierten Dateien nicht die Versionen von protoc
und ts-proto
, mit denen die Datei generiert wird. Diese Option ist normalerweise auf true
festgelegt, sodass Dateien die verwendeten Versionen auflisten.
Mit --ts_proto_opt=outputSchema=true
werden Meta -Typierungen generiert, die später in anderen Codegeneratoren verwendet werden können.
Mit --ts_proto_opt=outputSchema=no-file-descriptor
werden Meta-Typierungen generiert, aber wir fügen den Dateideskriptor nicht in das generierte Schema ein. Dies ist nützlich, wenn Sie versuchen, die Größe des generierten Schemas zu minimieren.
Mit --ts_proto_opt=outputSchema=const
werden Meta-Typierungen as const
generiert, wodurch der Typ-Safe-Zugriff auf alle Eigenschaften ermöglicht wird. (Funktioniert nur mit TypeScript 4.9 und up, da es auch den Bediener satisfies
Bedieners verwendet.) Kann mit der Option no-file-descriptor
( outputSchema=const,outputSchema=no-file-descriptor
) kombiniert werden, um den Dateideskriptor nicht in das generierte Schema aufzunehmen.
Mit --ts_proto_opt=outputTypeAnnotations=true
erhält jede Nachricht ein Feld $type
das ihren vollqualifizierten Namen enthält. Sie können --ts_proto_opt=outputTypeAnnotations=static-only
verwenden, um sie aus der interface
wegzulassen, oder --ts_proto_opt=outputTypeAnnotations=optional
um sie zu einer optionalen Eigenschaft für die interface
zu machen. Die letztere Option ist möglicherweise nützlich, wenn Sie das Feld $type
für den Laufzeittyp verwenden möchten, um Antworten von einem Server zu überprüfen.
Mit --ts_proto_opt=outputTypeRegistry=true
wird die Typregistrierung generiert, mit der Nachrichtentypen durch einen vollqualifizierten Namen behoben werden können. Außerdem erhält jede Nachricht ein Feld $type
das ihren vollqualifizierten Namen enthält.
Mit --ts_proto_opt=outputServices=grpc-js
gibt TS-Proto Service-Definitionen und Server- / Client-Stubs im GRPC-JS-Format aus.
Mit --ts_proto_opt=outputServices=generic-definitions
, wird TS-Proto generische (Framework-Agnostic) Service-Definitionen ausgegeben. Diese Definitionen enthalten Deskriptoren für jede Methode mit Links zum Anfordern von und Antworttypen, mit denen Server- und Client -Stubs zur Laufzeit generiert werden können, und auch starke Typen für sie zum Kompilierungszeit. Ein Beispiel für eine Bibliothek, die diesen Ansatz verwendet, ist netter-GRPC.
Mit --ts_proto_opt=outputServices=nice-grpc
gibt TS-Proto Server- und Client-Stubs für netter-GRPC aus. Dies sollte zusammen mit generischen Definitionen verwendet werden, dh Sie sollten zwei Optionen angeben: outputServices=nice-grpc,outputServices=generic-definitions
.
Mit --ts_proto_opt=metadataType=Foo@./some-file
Fügen Sie der generischen Service-Definition ein generisches (Framework-Agnostic) -Metadatenfeld hinzu.
Mit --ts_proto_opt=outputServices=generic-definitions,outputServices=default
, gibt TS-Proto sowohl generische Definitionen als auch Schnittstellen aus. Dies ist nützlich, wenn Sie sich auf die Schnittstellen verlassen möchten, aber auch zur Laufzeit über einige Reflexionsfunktionen verfügen.
Mit --ts_proto_opt=outputServices=false
oder =none
, gibt TS-Proto keine Servicedefinitionen aus.
Mit --ts_proto_opt=rpcBeforeRequest=true
, fügt TS-Proto der RPC-Schnittstellendefinition mit der Signatur eine Funktionsdefinition hinzu beforeRequest(service: string, message: string, request: <RequestType>)
. Es wird auch automatisch outputServices=default
festlegen. Jede der Methoden des Dienstes rufen beforeRequest
vor der Ausführung der Anfrage an.
Mit --ts_proto_opt=rpcAfterResponse=true
fügt TS-Proto der RPC-Schnittstellendefinition mit der Signatur: afterResponse(service: string, message: string, response: <ResponseType>)
eine Funktionsdefinition hinzu. Es wird auch automatisch outputServices=default
festlegen. Jede der Methoden des Dienstes rufen nach der afterResponse
der Antwort nach der Reaktion auf.
Mit --ts_proto_opt=rpcErrorHandler=true
fügt der RPC-Schnittstellendefinition mit der Signatur: handleError(service: string, message: string, error: Error)
eine Funktionsdefinition hinzu. Es wird auch automatisch outputServices=default
festlegen.
Mit --ts_proto_opt=useAbortSignal=true
akzeptieren die generierten Dienste eine AbortSignal
um RPC -Anrufe zu stornieren.
Mit --ts_proto_opt=useAsyncIterable=true
werden die generierten Dienste AsyncIterable
anstelle von Observable
verwenden.
Mit --ts_proto_opt=emitImportedFiles=false
, wird TS-Protoo nicht google/protobuf/*
-Dateien aussetzt, es sei denn, Sie explizit Dateien zum protoc
wie dieses protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto my_message.proto google/protobuf/duration.proto
addieren. protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto my_message.proto google/protobuf/duration.proto
Mit --ts_proto_opt=fileSuffix=<SUFFIX>
wird TS-Proto generierte Dateien mithilfe des angegebenen Suffix emittiert. Eine helloworld.proto
-Datei mit fileSuffix=.pb
würde wie helloworld.pb.ts
generiert. Dies ist ein übliches Verhalten in anderen Protoc -Plugins und bietet eine Möglichkeit, alle generierten Dateien schnell zu blühen.
Mit --ts_proto_opt=importSuffix=<SUFFIX>
wird TS-Proto Dateiimporte mit dem angegebenen Suffix emittieren. Ein Import von helloworld.ts
mit fileSuffix=.js
würde import "helloworld.js"
. Die Standardeinstellung besteht darin, ohne Dateierweiterung zu importieren. Unterstützt von TypeScript 4.7.x und Up.
Mit --ts_proto_opt=enumsAsLiterals=true
werden die generierten Enum-Typen mit as const
aufgeblieben.
Mit --ts_proto_opt=useExactTypes=false
, werden die generierten fromPartial
und create
keine genauen Typen verwendet.
Das Standardverhalten ist useExactTypes=true
, was fromPartial
und create
den genauen Typ für sein Argument, um die Typscript -Unbekannten unbekannte Eigenschaften abzulehnen.
Mit --ts_proto_opt=unknownFields=true
, werden alle unbekannten Felder analysiert und als Arrays von Puffern ausgegeben.
Mit --ts_proto_opt=onlyTypes=true
, werden nur Typen emittiert und Importe für long
und protobufjs/minimal
werden ausgeschlossen.
Dies entspricht der Einstellung outputJsonMethods=false,outputEncodeMethods=false,outputClientImpl=false,nestJs=false
Mit --ts_proto_opt=usePrototypeForDefaults=true
wickelt der generierte Code neue Objekte mit Object.create
ein.
Auf diese Weise können Code -Überprüfungen erkennen, wann Standardwerte angewendet wurden. Dies ist aufgrund des Verhaltens von Proto3 in der Regel nur nützlich, um mit Proto2 -Nachrichten zu interagieren.
Wenn es aktiviert ist, werden Standardwerte von einem Prototyp geerbt, sodass Code Object verwenden kann.
Beachten Sie, dass dies, wie angegeben ist, Objekt.Keys keine Set-by-Default-Felder enthalten. Wenn Sie also Code haben, das über generische Meldetasten iteriert, muss er auch über Tasten, die vom Prototyp geerbt wurden, wiederholen.
Mit --ts_proto_opt=useJsonName=true
, wird json_name
in Protofilen definiert anstelle von Nachrichtenfeldnamen verwendet.
Mit --ts_proto_opt=useJsonWireFormat=true
spiegelt der generierte Code die JSON -Darstellung von Protobuf -Nachrichten wider.
Erfordert onlyTypes=true
. Impliziert useDate=string
und stringEnums=true
. Diese Option besteht darin, Typen zu generieren, die direkt mit Marshalling/Unmarshalling -Protobuf -Nachrichten als JSON verwendet werden können. Möglicherweise möchten Sie auch useOptionals=all
festlegen, da GRPC -Gateways nicht erforderlich sind, um den Standardwert für Skalarwerte zu senden.
Mit --ts_proto_opt=useNumericEnumForJson=true
wird der JSON -Konverter ( toJSON
) Enum -Werte eher als int als als ein String -Literal codieren.
Mit --ts_proto_opt=initializeFieldsAsUndefined=false
werden alle optionalen Feldinitialisierer aus den generierten Basisinstanzen weggelassen.
Mit --ts_proto_opt=disableProto2Optionals=true
, werden alle optionalen Felder auf Proto2 -Dateien nicht so eingestellt, dass sie optional sind. Bitte beachten Sie, dass dieses Flag in erster Linie zur Erhaltung von Ts-Proto-Legenhandhabung von Proto2-Dateien dient, um Veränderungen zu vermeiden, und es ist daher nicht beabsichtigt, sich vorwärts zu bewegen.
Mit --ts_proto_opt=disableProto2DefaultValues=true
werden alle Felder in Proto2 -Dateien, die einen Standardwert angeben, diesen Standardwert nicht tatsächlich verwenden. Bitte beachten Sie, dass dieses Flag in erster Linie zur Erhaltung von Ts-Proto-Legenhandhabung von Proto2-Dateien dient, um Veränderungen zu vermeiden, und es ist daher nicht beabsichtigt, sich vorwärts zu bewegen.
Mit --ts_proto_opt=Mgoogle/protobuf/empty.proto=./google3/protobuf/empty
, ('m' bedeutet 'importmapping', ähnlich wie Protoc-Gen-go), der generierte Code-Importpfad für ./google/protobuf/empty.ts
spiegelt den überschriebenen Wert wider:
Mfoo/bar.proto=@myorg/some-lib
wird foo/bar.proto
import ... from '@myorg/some-lib'
.
Mfoo/bar.proto=./some/local/lib
wird foo/bar.proto
import ... from './some/local/lib'
.
Mfoo/bar.proto = import ... from 'some-module/some-lib'
Mfoo/bar.proto=some-modules/some-lib
wird foo/bar.proto
.
HINWEIS : Verwendungen werden akkumuliert, sodass in der Form von --ts_proto_opt=M... --ts_proto_opt=M...
(One ts_proto_opt
pro Mapping) erwartet werden.
HINWEIS : Proto -Dateien, die zu den zugeordneten Importen übereinstimmen , werden nicht generiert .
Mit --ts_proto_opt=useMapType=true
wird der generierte Code für Protobuf map<key_type, value_type>
Map<key_type, value_type>
der den JavaScript -Kartentyp verwendet.
Das Standardverhalten ist useMapType=false
, wodurch der Code für Protobuf map<key_type, value_type
mit dem Schlüsselwertpaar wie {[key: key_type]: value_type}
generiert wird.
Mit --ts_proto_opt=useReadonlyTypes=true
werden die generierten Typen unter Verwendung readonly
-Modifikators von TypeScript als unveränderlich deklariert.
Mit --ts_proto_opt=useSnakeTypeName=false
entfernen das Schlangengehäuse von Typen.
Beispielprotobuf
Nachrichtenfeld {Nachrichtenelement {Nachrichtenbild {Enum -Ausrichtung {links = 1; Mitte = 2; Rechts = 3; } } } }
Standardmäßig ist dies aktiviert, wodurch ein Typ von Box_Element_Image_Alignment
generiert wird. Durch Deaktivieren dieser Option ist der generierte Typ BoxElementImageAlignment
.
Mit --ts_proto_opt=outputExtensions=true
enthält der generierte Code Proto2 -Erweiterungen
setExtension
getExtension
/Decodod -Methoden outputEncodeMethods
unknownFields=true
Option outputEncodeMethods
.
Mit --ts_proto_opt=outputIndex=true
werden Indexdateien basierend auf den Proto -Paket -Namespaces generiert.
Dies deaktiviert exportCommonSymbols
, um Namenskollisionen auf den gemeinsamen Symbolen zu vermeiden.
Mit --ts_proto_opt=emitDefaultValues=json-methods
wird die generierte TOJSON-Methode Skalare wie 0
und ""
als JSON-Felder aussendet.
Mit --ts_proto_opt=comments=false
werden Kommentare nicht von den Protodateien in den generierten Code kopiert.
Mit --ts_proto_opt=bigIntLiteral=false
verwendet der generierte Code BigInt("0")
anstelle von 0n
für Bigint -Literale. Bigint -Literale werden von TypeScript nicht unterstützt, wenn die Option "Ziel" -Kompiler auf etwas älteres als "ES2020" eingestellt wird.
Mit --ts_proto_opt=useNullAsOptional=true
, undefined
Werte werden in null
konvertiert, und wenn Sie in Ihrer .proto
-Datei optional
Etikett verwenden, hat das Feld auch undefined
Typ. Zum Beispiel:
Mit --ts_proto_opt=typePrefix=MyPrefix
haben die generierten Schnittstellen, Enums und Fabriken ein Präfix von MyPrefix
in ihren Namen.
Mit --ts_proto_opt=typeSuffix=MySuffix
haben die generierten Schnittstellen, Enums und Fabriken ein Suffix von MySuffix
in ihren Namen.
Message ProfileInfo {int32 id = 1; String bio = 2; String Telefon = 3; } Nachrichtenabteilung {int32 id = 1; String name = 2; } Nachrichtennutzer {int32 id = 1; String username = 2;/* profileInfo ist in TypeScript optional. Der Typ ist profilinfo | NULL | Undefiniert ist dies in Fällen erforderlich, in denen Sie keinen Wert für das Profil bereitstellen möchten. */Optionales ProfileInfo Profil = 3;/* Die Abteilung akzeptiert nur einen Abteilungsart oder Null. Dies bedeutet, dass Sie es null übergeben müssen, wenn kein Wert verfügbar ist. */Abteilung Abteilung = 4; }
Die erzeugten Schnittstellen werden:
Export -SchnittstellenprofileInfo { ID: Nummer; Bio: String; Telefon: String;} Export -Schnittstelle Abteilung { ID: Nummer; Name: String;} Benutzeroberfläche Benutzer { ID: Nummer; Benutzername: String; Profil ?: Profilinfo | NULL | undefiniert; // Überprüfen Sie diesen Abteilung: Abteilung | Null; // Überprüfen Sie diesen}
Mit --ts_proto_opt=noDefaultsForOptionals=true
werden undefined
primitive Werte nicht gemäß der Protobuf -Spezifikation standardmäßig. Im Gegensatz zum Standardverhalten wird ein Feld, wenn es auf seinen Standard -Standardwert eingestellt ist, codiert, sodass es über den Kabel gesendet und von undefinierten Werten unterschieden werden kann. Wenn beispielsweise eine Nachricht keinen booleschen Wert festlegt, würde dies normalerweise in Bezug auf false
standardmäßig sein, was sich als nicht definiert unterscheidet.
Diese Option ermöglicht es der Bibliothek, kompatible Weise mit der von Square/Block verwalteten und Verwendung der Drahtimplementierung zu handeln. Hinweis: Diese Option sollte nur in Kombination mit anderen Client/Server-Code verwendet werden, die mit der aktivierten Option mit Draht oder TS-Proto generiert werden.
Wir haben eine großartige Möglichkeit, mit NestJs zusammenzuarbeiten. ts-proto
generiert interfaces
und decorators
für Ihren Controller, Client. Weitere Informationen finden Sie im Nestjs Readme.
Wenn Sie ts-proto
für jede Änderung einer Proto-Datei ausführen möchten, müssen Sie ein Tool wie Chokidar-Cli verwenden und als Skript in package.json
verwenden:
"Proto: generieren": "Protoc - -ts_proto_out =. C "NPM Run Proto: generieren" "
ts-proto
ist Agnostiker des RPC -Frameworks - wie Sie Ihre Daten an und von Ihrer Datenquelle übertragen, liegt bei Ihnen. Die generierten Client -Implementierungen erwarten alle einen rpc
-Parameter, welcher Typ so definiert ist:
Schnittstelle RPC { Request (Service: String, Methode: String, Daten: Uint8Array): Versprechen <Uint8Array>;}
Wenn Sie mit GRPC arbeiten, kann eine einfache Implementierung so aussehen:
const conn = new grpc.client ( "Localhost: 8765", grpc.credentials // Konventionell in GRPC sieht der Anforderungsweg aus wie // "package.names.serviceName/methodname", // Wir konstruieren daher eine solche Zeichenfolge const path = `/$ {service}/$ {method}`; Neues Versprechen zurückgeben ((Resolve, ablehnen) => {// makeUnaryRequest überträgt das Ergebnis (und den Fehler) mit einem Rückruf // diese in ein Versprechen verwandeln! const resultcallback: unarycallback <any> = (err, res) => {wenn (err) {return reject (err); , resultcallback); });}; const rpc: rpc = {request: sendRequest};
Ein großes Lob an unsere Sponsoren:
NGROK finanzierte die anfängliche GRPC-Web-Unterstützung von TS-Proto.
Wenn Sie TS-Proto-Anpassungen oder Priority-Support für Ihr Unternehmen benötigen, können Sie mich per E-Mail an die Ping halten.
In diesem Abschnitt wird beschrieben, wie man direkt zu TS-Proto beiträgt, dh es ist nicht erforderlich, um ts-proto
im protoc
auszuführen oder das generierte Typenkript zu verwenden.
Anforderungen
Docker
yarn
npm install -g yarn
Aufstellen
Die folgenden Befehle gehen davon aus, dass Sie Docker installiert haben. Wenn Sie OS X verwenden, installieren Sie CoreUtils und brew install coreutils
.
Schauen Sie sich das Repository für den neuesten Code an.
Führen Sie yarn install
um die Abhängigkeiten zu installieren.
Führen Sie yarn build:test
um die Testdateien zu generieren.
Dies führt die folgenden Befehle aus:
proto2ts
-Führen Sie ts-proto
in der integration/**/*.proto
Dateien aus, um .ts
Dateien zu generieren.
proto2pbjs
- generiert eine Referenzimplementierung unter Verwendung pbjs
zur Prüfkompatibilität.
Führen Sie yarn test
Workflow
Fügen Sie einen Integrationstest für Ihren Anwendungsfall hinzu/aktualisieren Sie
Sie können auch yarn watch
laufen lassen, und es sollte "einfach das Richtige tun"
Machen Sie eine neue integration/your-new-test/parameters.txt
mit den erforderlichen ts_proto_opt
Params
integration/your-new-test/your-new-test.proto
Entweder finden Sie einen vorhandenen integration/*
Test ts_proto_opt
der nahe genug an Ihrem Anwendungsfall ist, z. B. über eine parameters.txt
Wenn Sie einen neuen Integrationstest erstellen:
Nach Änderungen an your-new-test.proto
oder einer vorhandenen integration/*.proto
Datei führen Sie yarn proto2bin
aus
Hinzufügen/Aktualisieren eines integration/your-new-test/some-test.ts
.
Ändern Sie die ts-proto
Code-Generierungslogik:
Oder yarn proto2ts your-new-test
um einen bestimmten Test neu zu Codegen neu
Wieder das Laufen yarn watch
sollte "einfach das Richtige tun"
Die wichtigste Logik ist in SRC/Main.TS.
Führen Sie nach Änderungen an src/*.ts
Dateien yarn proto2ts
aus, um alle Integrationstests neu zu codieren
Führen Sie yarn test
aus, um zu überprüfen, ob Ihre Änderungen alle vorhandenen Tests bestehen
Eine PR eingehen und einreichen
Manchmal ist die Überprüfung des generierten Codes verpönt, aber angesichts der Hauptaufgabe von TS-Proto besteht darin, Code zu generieren, es ist hilfreich, die Codgen-Diffs in PRs zu sehen
Führen Sie yarn format
aus, um die TypeScript -Dateien zu formatieren.
git add
*.bin
*.proto
*.ts
integration/your-new-test
Testen in Ihren Projekten
Sie können Ihre lokalen TS-Proto-Änderungen in Ihren eigenen Projekten testen, indem Sie yarn add ts-proto@./path/to/ts-proto
, solange Sie yarn build
manuell ausführen.
Dockerisierter Protoc
Das Repository enthält eine Dockerized-Version von protoc
, die in Docker-compose.yml konfiguriert ist.
Es kann nützlich sein, falls Sie das Plugin mit einer bekannten protoc
-Version manuell aufrufen möchten.
Verwendung:
# Geben Sie den Protoc -Alias in Ihre Shell ein. Aliases.sh# Rennen Sie Protoc wie gewohnt. Das TS-Proto-Verzeichnis ist in /ts-proto.protoc --plugin =/ts-proto/protoc -gen-ts_proto ---ts_proto_out =./Output -i =./Protos ./protoc/*..proto# oder verfügbar Verwenden Sie den Ts-Protoc-Alias, der den Plugin-Pfad für Sie angibt.
Alle Pfade müssen relative Pfade im aktuellen Arbeitsverzeichnis des Hosts sein. ../
ist nicht erlaubt
Innerhalb des Docker-Containers ist der absolute Pfad zur Projektwurzel /ts-proto
Der Container montiert das aktuelle Arbeitsverzeichnis in /host
und setzt es als Arbeitsverzeichnis.
Sobald aliases.sh
bezogen ist, können Sie den protoc
-Befehl in jedem Ordner verwenden.
Der Name des TS/ES6 -Moduls ist das Protopaket
Unterstützen Sie die String-basierte Codierung der Dauer in fromJSON
/ toJSON
Machen Sie oneof=unions-value
das Standardverhalten in 2.0
forceLong=long
forceLong
Machen Sie esModuleInterop=true
die Standardeinstellung in 2.0
Standardmäßig modelliert TS-Proto-Modelle " oneof
" in der Nachricht, z. B. eine Nachricht wie:
Message foo {Oneof ENDE_FIELD {String field_a = 1; String field_b = 2; } }
Erzeugt einen Foo
-Typ mit zwei Feldern: field_a: string | undefined;
und field_b: string | undefined
.
Mit dieser Ausgabe müssen Sie sowohl if object.field_a
als if object.field_b
überprüfen, und wenn Sie einen festlegen, müssen Sie daran denken, den anderen zu verunsichern.
Stattdessen empfehlen wir die Verwendung der Option oneof=unions-value
, die die Ausgabe zu einem algebraischen Datentyp/ADT ändert:
Schnittstelle yourMessage { entweder Feld ?: {$ case: "field_a"; Wert: String} | {$ case: "field_b"; Wert: String};}
Da dies automatisch nur eines von field_a
oder field_b
"festgelegt" nach einem Zeitpunkt erzwingt, da die Werte im Feld eitherField
Feld gespeichert sind, die jeweils nur einen einzelnen Wert haben können.
(Beachten Sie, dass eitherField
optional b/c oneof
in Protobuf bedeutet, dass "höchstens ein Feld" eingestellt ist und nicht bedeutet, dass eines der Felder festgelegt werden muss .)
In TS-Proto's derzeit nicht planter 2.x Release wird oneof=unions-value
zum Standardverhalten.
Es gibt auch eine oneof=unions
-Option, die eine Gewerkschaft generiert, bei der die Feldnamen in jeder Option enthalten sind:
Schnittstelle yourMessage { entweder Feld ?: {$ case: "field_a"; Field_a: String} | {$ case: "field_b"; field_b: String};}
Dies wird nicht mehr empfohlen, da es schwierig sein kann, Code und Typen zu schreiben, um mehrere Oneof -Optionen zu verarbeiten:
Die folgenden Helfertypen erleichtern möglicherweise einfacher, mit den von oneof=unions
erzeugten Typen zu arbeiten, obwohl sie im Allgemeinen nicht benötigt werden, wenn Sie oneof=unions-value
verwenden.
/** extrahiert alle Fallnamen aus einem Ein -Of -Feld. */Typ OneOfCasen <T> = T erweitert {$ case: inferen u erweitert String}? U: Niemals;/** extrahiert eine Vereinigung aller Werttypen aus einem Ein -Of -Feld*/Typ OneofValues <T> = T erweitert {$ case: inferen u erweitert String; [Schlüssel: String]: Unbekannt}? T [u]: niemals;/** extrahiert den spezifischen Typ eines Eins von einem von seinem Feldnamen*/Typ Oneofcase <t, k erweitert sich um eins. $ case: k; [Schlüssel: String]: Unbekannt;} ? T : Niemals;/** extrahiert den spezifischen Typ eines Werttyps aus einem Ein -Of -Feld*/type OfValue <t, k erweitert sich eins. $ case: inferen u erweitert k; [Schlüssel: String]: Unbekannt;} ? T [u] : niemals;
Zum Vergleich: Die Äquivalente für oneof=unions-value
:
/** extrahiert alle Fallnamen aus einem Ein -Of -Feld. */type Ofcases <T> = T ['$ case'];/** extrahiert eine Vereinigung aller Werttypen aus einem Ein -Of -Feld*/Typ OneofValues <T> = T ['Wert'];/** Extrakte Der spezifische Typ eines One -of -Falls basierend auf seinem Feldnamen */Typ OneofCase <t, k erweitert sich eins. $ case: k; [Schlüssel: String]: Unbekannt;} ? T : Niemals;/** extrahiert den spezifischen Typ eines Werttyps aus einem Ein -Of -Feld*/type OfValue <t, k erweitert sich eins. $ case: inferen u erweitert k; Wert: unbekannt;} ? T [u] : niemals;
Im Kernprotobuf (und damit auch ts-proto
) werden Werte, die nicht festgelegt oder dem Standardwert entsprechen, nicht über den Kabel gesendet.
Beispielsweise ist der Standardwert einer Nachricht undefined
. Primitive Typen nehmen ihren natürlichen Standardwert an, z. B. string
lautet ''
, number
ist 0
usw.
Protobuf hat dieses Verhalten ausgewählt/erzwungen, da es die Weiterleitung der Kompatibilität ermöglicht, da primitive Felder immer einen Wert haben, selbst wenn es von veralteten Mitteln weggelassen wird.
Dies ist gut, aber es bedeutet auch, dass Standardwerte und nicht festgelegte Werte in ts-proto
-Feldern nicht unterschieden werden können. Es ist nur so, wie Protobuf funktioniert.
Wenn Sie primitive Felder benötigen, in denen Sie Set/Unstet erkennen können, siehe Wrapper -Typen.
Codieren / decodieren
ts-proto
befolgt die Protobuf-Regeln und gibt bei der Dekodierung immer Standardwerte für Unets-Felder zurück, während sie bei serialisiertem Binärformat aus der Ausgabe weggelassen werden.
syntax = "proto3"; message foo {String bar = 1; }
Protobufbytes; // Angenommen, dies ist ein leeres Foo -Objekt in Protobuf -binärem formatfoo.decode (protobufbytes); // => {bar: ''}
Foo.encode ({bar: ""}); // => {}, schreibt ein leeres Foo -Objekt im Protobuf -Binärformat
von json / tojson
Das Lesen von JSON initialisiert auch die Standardwerte. Da Absender entweder nicht festgelegte Felder weglassen oder auf den Standardwert einstellen können, verwenden Sie fromJSON
um die Eingabe zu normalisieren.
Foo.fromjson ({}); // => {bar: ''} foo.fromjson ({bar: ""}); // => {bar: ''} foo.fromjson ({bar: "baz"}); // => {bar: 'baz'}
Beim Schreiben von JSON normalisiert ts-proto
die Nachrichten, indem sie nicht festgelegte Felder und Felder auf ihre Standardwerte einstellen.
Foo.tojson ({}); // => {} foo.tojson ({bar: undefined}); // => {} foo.tojson ({bar: ""}); // => {} - Hinweis: Auslösen des Standardwerts, wie erwartet, tojson ({bar: "baz"}); // => {bar: 'baz'}
Protobuf verfügt über mehrere vordefinierte Nachrichtendefinitionen, die als "bekannte Typen" bezeichnet werden. Ihre Interpretation wird durch die Protobuf -Spezifikation definiert, und es wird erwartet, dass Bibliotheken diese Nachrichten in entsprechende native Typen in der Zielsprache umwandeln.
ts-proto
konvertiert diese Nachrichten derzeit automatisch in ihre entsprechenden nativen Typen.
Google.Protobuf.BoolValue ⇆ boolean
Google.Protobuf.ByTesValue ⇆ 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
(dh number | string | boolean | null | array | object
)
Google.Protobuf.struct ⇆ { [key: string]: any }
Wrapper -Typen sind Nachrichten, die ein einzelnes primitives Feld enthalten, und können in .proto
-Dateien mit import "google/protobuf/wrappers.proto"
werden.
Da es sich um Nachrichten handelt, ist ihr Standardwert undefined
, sodass Sie bei Verwendung von Wrapper -Typen nicht festgelegte Primitive von ihren Standardwerten unterscheiden können. ts-proto
generiert diese Felder als <primitive> | undefined
.
Zum Beispiel:
// protObufsyntax = "proto3"; importieren "google/protobuf/wrappers.proto"; message examPremessage {google.protobuf.stringValue name = 1; }
// typecriptInterface examPeMessage { Name: String | undefiniert;}
Bei der Codierung einer Nachricht wird der primitive Wert wieder in den entsprechenden Wrapper -Typ konvertiert:
BeispielePremessage.enCode ({Name: "foo"}); // => {name: {value: 'foo'}} in Binary
Beim Aufrufen von Tojson wird der Wert nicht konvertiert, da Wrapper -Typen in JSON idiomatisch sind.
BeispielPlemessage.tojson ({Name: "foo"}); // => {name: 'foo'}
Die Sprache und Typen von Protobuf reichen nicht aus, um alle möglichen JSON -Werte darzustellen, da JSON Werte enthalten kann, deren Typ im Voraus unbekannt ist. Aus diesem Grund bietet Protobuf mehrere zusätzliche Typen an, um willkürliche JSON -Werte darzustellen.
Diese werden als Strukturarten bezeichnet und können in .proto
-Dateien mit import "google/protobuf/struct.proto"
importiert werden.
Google.Protobuf.Value ⇆ any
Dies ist der allgemeinste Typ und kann jeden JSON -Wert darstellen (dh number | string | boolean | null | array | object
).
Google.Protobuf.ListValue ⇆ any[]
Ein JSON -Array darstellen
Google.Protobuf.struct ⇆ { [key: string]: any }
Ein JSON -Objekt darstellen
ts-proto
konvertiert automatisch zwischen diesen Strukturtypen und ihren entsprechenden JSON-Typen.
Beispiel:
// protObufsyntax = "proto3"; importieren "google/protobuf/struct.proto"; Message ExamPremessage {google.Protobuf.Value alles = 1; }
// typecriptInterface examPeMessage { Alles: Any | undefiniert;}
Um einen in einer Nachricht eingebetteten JSON -Wert zu codieren, wandelt sie sie in einen Strukturart um:
BeispielePremessage.enCode ({Anything: {Name: "Hallo"}});/* Ausgibt die folgende Struktur, die in Protobuf -Binärformat codiert ist: {Anything: value {structValue = struct {fields = [mapEntry {key = "name", value = value {stringValue = "hello"}]}}}}}*/evitreMessage.encode ({Anything: true});/* Ausgibt die folgende Struktur, die in Protobuf -Binärformat codiert ist: {Anything: value {boolvalue = true}} */
Die Darstellung von google.protobuf.Timestamp
ist durch das useDate
-Flag konfigurierbar. Das useJsonTimestamp
-Flag steuert die Genauigkeit, wenn useDate
, wenn sie false
werden.
Protobuf bekannter Typ | Standard/ useDate=true | useDate=false | useDate=string | useDate=string-nano |
---|---|---|---|---|
google.protobuf.Timestamp | Date | { seconds: number, nanos: number } | string | string |
Bei Verwendung useDate=false
und useJsonTimestamp=raw
Timestamp wird als { seconds: number, nanos: number }
dargestellt, hat jedoch eine Nanosekundengenauigkeit.
Bei Verwendung useDate=string-nano
wird der Zeitstempel als ISO-String mit Nanosekunden-Präzision 1970-01-01T14:27:59.987654321Z
dargestellt und stützt sich auf die Nano-Datum-Bibliothek für die Konvertierung. Sie müssen es in Ihrem Projekt installieren.
Die Nummern werden standardmäßig als klare JavaScript number
s angenommen.
Dies ist in Ordnung für Protobuf-Typen wie int32
und float
, aber 64-Bit-Typen wie int64
können nicht 100% durch JavaScript- number
dargestellt werden, da int64
größere/kleinere Werte als number
haben kann.
Die Standardkonfiguration von TS-Proto (die für forceLong=number
ist) besteht darin, number
für 64-Bit-Felder noch zu verwenden und dann einen Fehler zu werfen, wenn ein Wert (zur Laufzeit) größer als Number.MAX_SAFE_INTEGER
ist. max_safe_integer.
Wenn Sie erwarten, dass Sie 64-Bit / höhere MAX_SAFE_INTEGER
-Werte verwenden, können Sie die Option TS-Proto forceLong
verwenden, die das Long NPM-Paket verwendet, um den gesamten Bereich von 64-Bit-Werten zu unterstützen.
Die Protobuf -Nummer -Typen sind auf JavaScript -Typen basierend auf der forceLong
-Konfigurationsoption zugeordnet:
Protobuf -Zahlentypen | Standard/ forceLong=number | forceLong=long | forceLong=string |
---|---|---|---|
doppelt | Nummer | Nummer | Nummer |
schweben | Nummer | Nummer | Nummer |
INT32 | Nummer | Nummer | Nummer |
INT64 | Nummer* | Lang | Saite |
Uint32 | Nummer | Nummer | Nummer |
Uint64 | Nummer* | Nicht signiert | Saite |
SINT32 | Nummer | Nummer | Nummer |
SINT64 | Nummer* | Lang | Saite |
behoben32 | Nummer | Nummer | Nummer |
fest 64 | Nummer* | Nicht signiert | Saite |
sfixed32 | Nummer | Nummer | Nummer |
sfixed64 | Nummer* | Lang | Saite |
Wobei (*) angibt, dass sie zur Laufzeit einen Fehler werfen könnten.
Erforderliche Primitive: Verwenden Sie AS-IS, dh string name = 1
.
Optionale Primitive: Verwenden Sie Wrapper -Typen, dh StringValue name = 1
.
Erforderliche Nachrichten: Nicht verfügbar
Optionale Nachrichten: Verwenden Sie AS-IS, dh SubMessage message = 1
.