ts-proto
transforme vos fichiers.proto
en fichiers de type idiomatiques fortement typés!
La version 2.x de TS-Proto a migré le sérialisation du protobuf de bas niveau que son encode
et sa méthode decode
utilisent à partir du package vénérable, mais vieillissant et stagnant, protobufjs
vers @bufbuild/protobuf
.
Si vous n'utilisez que les méthodes encode
et decode
, cela devrait être en grande partie un changement non révolutionnaire.
Cependant, si vous avez utilisé un code qui a utilisé les anciennes classes Writer
ou Reader
protobufjs
, vous devrez mettre à jour votre code pour utiliser les nouvelles classes @bufbuild/protobuf
:
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
Si la migration vers @bufbuild/protobuf
est un bloqueur pour vous, vous pouvez épingler votre version ts-proto
vers 1.x
Avertissement et excuses: j'avais l'intention de publier TS-Proto 2.x en tant que version alpha, mais je n'ai pas obtenu la configuration de libération sémantique correcte, et donc TS-Proto 2.x a été publié en tant que version majeure sans alpha approprié / cycle bêta.
Si vous pouviez déposer des rapports (ou mieux PRS!) Pour tout problème que vous rencontrez pendant que la version est encore fraîche, ce serait grandement apprécié.
Tous les conseils ou astuces pour les autres sur la migration seraient également appréciés!
ts-proto
Table des matières
Aperçu
Start
Bouf
ESM
Objectifs
Non-Goals
Exemples de types
Points forts
Prévention automatique / N + 1
Usage
Options prises en charge
Support NESTJS
Mode de surveillance
Implémentation de base GRPC
Sponsors
Développement
Hypothèses
Faire
ONEOF TRADICATION
Valeurs par défaut et champs non set
Types bien connus
Types d'emballage
Types JSON (types de structure)
Horodatage
Types de nombres
État actuel des valeurs facultatives
TS-Proto génère des types de typeScript à partir de schémas Protobuf.
C'est-à-dire donner une person.proto
schéma comme:
Message Person {String name = 1; }
TS-Proto générera un fichier person.ts
comme:
Personne d'interface { Nom: String} const Person = { Encoder (personne): écrivain {...} Decode (lecteur): personne {...} tojson (personne): inconnu {...} Fromjson (données): personne {...}}
Il connaît également les services et générera également des types pour eux, c'est-à-dire:
Interface d'exportation pingservice { ping (demande: pingrequest): promesse <pingResponse>;}
Il générera également des implémentations clients de PingService
; Actuellement, Twirp, GRPC-WEB, GRPC-JS et NESTJS sont pris en charge.
npm install ts-proto
protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=. ./simple.proto
(Notez que le nom du paramètre de sortie, ts_proto_out
, est nommé sur le suffixe du nom du plugin, IE "TS_Proto" Suffixe dans le paramètre --plugin=./node_modules/.bin/protoc-gen-ts_proto
devient le _out
Prefix, Conventions CLI de protoc
.)
Sur Windows, utilisez protoc --plugin=protoc-gen-ts_proto=".node_modules.binprotoc-gen-ts_proto.cmd" --ts_proto_out=. ./simple.proto
(voir # 93)
Assurez-vous que vous utilisez un protoc
moderne (voir les instructions d'installation pour votre plate-forme, c'est-à-dire protoc
v 3.0.0
ne prend pas en charge l'indicateur _opt
Cela générera des fichiers source *.ts
pour les types *.proto
donnés.
Si vous souhaitez emballer ces fichiers source dans un package NPM pour distribuer aux clients, exécutez simplement tsc
sur eux comme d'habitude pour générer les fichiers .js
/ .d.ts
et déployer la sortie en tant que package NPM ordinaire.
Si vous utilisez BUF, passez strategy: all
dans votre fichier buf.gen.yaml
(docs).
Version: V1Plugins: - Nom: Tsout: ../gen/tsstrategy: allpath: ../node_modules/ts-proto/protoc-gen-ts_proto
Pour empêcher buf push
de lire les fichiers .proto
non pertinents, configurez buf.yaml
comme tel:
build: exclut: [node_modules]
Vous pouvez également utiliser le plugin officiel publié dans le registre BUF.
Version: V1Plugins: - Plugin: buf.build/community/stephenh-ts-protoout: ../gen/tsopt: - OutputServices = ... - useExactTypes = ...
Si vous utilisez une configuration TS moderne avec esModuleInterop
ou en cours d'exécution dans un environnement ESM, vous devrez passer ts_proto_opt
S de:
esModuleInterop=true
si utilise esModuleInterop
dans votre tsconfig.json
, et
importSuffix=.js
Si l'exécution du code TS-Proto généré dans un environnement ESM
En termes de code que ts-proto
génère, les objectifs généraux sont:
Types de typescript / ES6 idiomatiques
ts-proto
est une rupture nette par rapport au code JS de protoc
Google / Java-esque intégré ou "Make .d.ts
File le *.js
Commentaires" Approche de protobufjs
(Techniquement, le package protobufjs/minimal
est utilisé pour la lecture / écriture des octets.)
Sortie de type dactylographié
Interfaces sur les classes
Dans la mesure du possible, les types ne sont que des interfaces, vous pouvez donc travailler avec des messages comme des hachages / structures de données réguliers.
Prend en charge uniquement Codegen *.proto
-to- *.ts
workflow, actuellement sans réflexion / chargement de fichiers dynamiques .proto
Notez que TS-Proto n'est pas un framework RPC prêt à l'emploi; Au lieu de cela, c'est plus un couteau suisse-armé (comme en témoignent ses nombreuses options de configuration), qui vous permet de construire exactement le cadre RPC que vous souhaitez en plus (c'est-à-dire que cela s'intègre le mieux à l'écosystème Protobuf de votre entreprise; pour le meilleur ou pour le pire , Protobuf RPC est encore un écosystème quelque peu fragmenté).
Si vous souhaitez un framework RPC prêt à l'emploi construit sur TS-Proto, il y a quelques exemples:
Nice-Grpc
starpc
(Remarque pour les contributeurs potentiels, si vous développez d'autres frameworks / mini-châssis, ou même des articles de blog / tutoriels, sur l'utilisation ts-proto
, nous sommes heureux de les lier.)
Nous ne prenons pas non plus les clients pour les clients google.api.http
sur Google Cloud basés sur Google Cloud, voir # 948 Si vous souhaitez soumettre un PR.
Les types générés sont des "données", c'est-à-dire:
Interface d'exportation Simple { Nom: String; Âge: nombre; CreateDat: Date | indéfini; Enfant: enfant | indéfini; État: Stateenum; petits-enfants: enfant []; pièces: nombre [];}
Ainsi que les méthodes d'usine encode
/ decode
:
Export Const Simple = { Create (BaseObject ?: Deeppartial <IMPIL>): Simple {...}, Encode (message: simple, écrivain: écrivain = writer.create ()): écrivain {...}, Decode (lecteur: lecteur, longueur ?: numéro): simple {...}, fromjson (objet: any): simple {...}, FromPartial (Object: Deeppartial <Imple>): Simple {...}, tojson (message: simple): inconnu {...},};
Cela permet une utilisation idiomatique TS / JS comme:
const bytes = Simple.encode ({name: ..., Âge: ..., ...}). Fination (); const Simple = Simple.decode (Reader.Create (Bytes)); const {nom, âge } = simple;
Ce qui peut atténuer considérablement l'intégration lors de la conversion vers / à partir d'autres couches sans créer de classe et appeler les bons Getters / Setters.
La tentative d'un pauvre homme de "veuillez nous redonner des types facultatifs"
Les types de wrapper Protobuf canonique, c'est-à-dire google.protobuf.StringValue
, sont mappés en valeurs facultatives, c'est-à-dire string | undefined
, ce qui signifie que pour les primitives, nous pouvons en quelque sorte prétendre que le système de type Protobuf a des types facultatifs.
( Mise à jour : TS-Proto prend désormais également en charge le mot-clé Proto3 optional
.)
Les horodatages sont mappés comme Date
(Configurable avec le paramètre useDate
.)
fromJSON
/ toJSON
Utilisez le format de codage JSON canonique Proto3 (par exemple, les horodatages sont des chaînes ISO), contrairement protobufjs
.
Objectid peut être cartographié sous le nom de mongodb.ObjectId
(Configurable avec le paramètre useMongoObjectId
.)
(Remarque: Ceci n'est actuellement pris en charge que par les clients Twirp.)
Si vous utilisez les clients de TS-Proto pour appeler des micro-services backend, similaires au problème N + 1 dans les applications SQL, il est facile pour les clients de micro-services (lors de la description d'une demande individuelle) déclencher par inadvertance plusieurs appels de RPC séparés pour "Get Book 1", "Get Book 2", "Get Book 3", qui devrait vraiment être regroupé en un seul "Get Books [1, 2, 3]" (en supposant que le backend prend en charge une méthode RPC axée sur les lots).
TS-Proto peut vous aider, et essentiellement automatiquement vos appels individuels "Get Book" dans des appels «Get Books» par lots.
Pour que TS-Proto le fasse, vous devez implémenter les méthodes RPC de votre service avec la convention de lots de:
Un nom de méthode de Batch<OperationName>
Le type d'entrée Batch<OperationName>
a un seul champ répété (c'est-à-dire repeated string ids = 1
)
Le type de sortie Batch<OperationName>
a soit A:
Un seul champ répété (c'est-à-dire repeated Foo foos = 1
) où l'ordre de sortie est le même que l'ordre ids
d'entrée , ou
Une carte de l'entrée dans une sortie (c.-à-d. map<string, Entity> entities = 1;
)
Lorsque TS-Proto reconnaît les méthodes de ce modèle, il créera automatiquement une version "non lot" de <OperationName>
pour le client, c'est-à-dire client.Get<OperationName>
, qui prend un seul ID et renvoie un seul résultat.
Cela fournit au code client l'illusion qu'il peut rendre les appels Get<OperationName>
de l'individu (qui sont généralement préférables / plus faciles lors de l'implémentation de la logique métier du client), mais l'implémentation réelle que TS-Proto finira finira par fabriquer Batch<OperationName>
appels au service backend.
Vous devez également activer le paramètre useContext=true
Build-Time, qui donne à toutes les méthodes du client un paramètre ctx
de style Go, avec une méthode getDataLoaders
qui permet à TS-Proto Cache / Resolve Spoped Dataloaders, qui fournit le lot de lots automatique fondamental Comportement de détection / rinçage.
Voir le fichier batching.proto
et les tests connexes pour des exemples / plus de détails.
Mais l'effet net est que TS-Proto peut fournir une prévention N + 1 de style SQL- / ORM pour les appels des clients, ce qui peut être essentiel en particulier dans les implémentations à volume élevé / hautement parallèles comme les passerelles frontales graphiques appelant les micro-services backend .
ts-proto
est un plugin protoc
, donc vous l'exécutez (soit directement dans votre projet, soit plus probablement dans votre pipeline de schéma mono-repo, c'est-à-dire comme ibotta ou à savoir):
Ajouter ts-proto
à votre package.json
Exécutez npm install
pour le télécharger
Invoquez protoc
avec un paramètre plugin
comme:
protoC --plugin = node_modules / ts-proto / protoc-gen-ts_proto ./batching.proto -i.
ts-proto
peut également être invoqué avec Gradle en utilisant le Protobuf-Gradle-Plugin:
protobuf { Les plugins {// `ts` peuvent être remplacés par n'importe quel nom de plugin inutilisé, par exemple` tsproto`ts { path = 'path / to / plugin'} } // Cette section nécessaire unique all (). Chaque {tâche -> tâche.plugins {// doit faire correspondre l'ID du plugin déclaré abrégé { Option 'foo = bar'} } } } }
Le code généré sera placé dans le répertoire Gradle Build.
Avec --ts_proto_opt=globalThisPolyfill=true
, ts-proto inclura un polyfill pour global this.
Par défaut est false
, c'est-à-dire que nous supposons que nous supposons globalThis
soit disponible.
Avec --ts_proto_opt=context=true
, les services auront un paramètre ctx
de style Go, ce qui est utile pour le traçage / logging / etc. Si vous n'utilisez pas l'API async_hooks
de Node pour des raisons de performances.
Avec --ts_proto_opt=forceLong=long
, tous les numéros de 64 bits seront analysés comme des instances de Long
(utilisant la bibliothèque longue).
Avec --ts_proto_opt=forceLong=string
, tous les numéros de 64 bits seront sortis sous forme de chaînes.
Avec --ts_proto_opt=forceLong=bigint
, tous les nombres de 64 bits seront sortis comme BigInt
s. Cette option utilise toujours la bibliothèque long
pour encoder / décoder en interne dans protobuf.js
, mais se convertit ensuite en / depuis BigInt
S dans le code généré par TS-Proto.
Le comportement par défaut est forceLong=number
, qui utilisera toujours en interne la bibliothèque long
pour encoder / décoder les valeurs sur le fil (vous verrez donc toujours une ligne util.Long = Long
ligne dans votre sortie), mais convertira les valeurs long
en number
automatiquement pour vous. Notez qu'une erreur d'exécution est lancée si, tout en faisant cette conversion, une valeur 64 bits est plus grande que ce qui peut être correctement stocké en number
.
Avec --ts_proto_opt=useJsTypeOverride
, les nombres 64 bits seront sortis comme FieldOption.jstype spécifiés sur le terrain. Cela a priorité sur l'option forceLong
fournie.
Avec --ts_proto_opt=esModuleInterop=true
change la sortie à être conforme esModuleInterop
.
Plus précisément, les Long
importations seront générées à titre d' import Long from 'long'
au lieu de import * as Long from 'long'
.
Avec --ts_proto_opt=env=node
ou browser
ou both
, TS-Proto fera des hypothèses spécifiques à l'environnement dans votre sortie. Cela par défaut, both
, ce qui ne fait aucune hypothèse spécifique à l'environnement.
L'utilisation node
modifie les types d' bytes
de Uint8Array
en Buffer
pour une intégration plus facile avec l'écosystème de nœud qui utilise généralement Buffer
.
Actuellement, browser
n'a pas de comportement spécifique autre que d'être "pas node
". Il sera probablement bientôt / à un moment donné.
Avec --ts_proto_opt=useOptionals=messages
(pour les champs de messages) ou --ts_proto_opt=useOptionals=all
(pour les champs de message et scalaire), les champs sont déclarés comme des touches facultatives, field?: Message
au lieu du field: Message | undefined
.
TS-Proto par défaut est useOptionals=none
car il:
Pour la prévention de la faute de frappe, les champs facultatifs facilitent les champs supplémentaires de se glisser dans un message (jusqu'à ce que nous obtenions des types exacts), c'est-à-dire:
interface SomeMessage { FirstName: String; LASTNAME: String;} // Déclaré avec un Data TypoConst = {FirstName: "A", LastTypo: "B"}; // avec useOptionals = Aucun, cela échoue correctement à la compilation; Si `LastName` était facultatif, il ne serait pas un message: SomeMessage = {... data};
Pour une API cohérente, si SomeMessage.lastName
est facultatif lastName?
, alors les lecteurs doivent vérifier deux conditions vides: a) est lastName
undefined
(b / c il a été créé en mémoire et laissé unset), ou b) est la chaîne vide lastName
(b / c nous lisons SomeMessage
hors du fil et, par La spécification proto3, initialisée lastName
à la chaîne vide)?
Pour garantir une initialisation appropriée, si plus tard SomeMessage.middleInitial
est ajouté, mais il est marqué comme middleInitial?
, vous pouvez avoir de nombreux sites d'appels dans le code de production qui devraient désormais passer middleInitial
pour créer un SomeMessage
valide, mais non.
Ainsi, entre la typo-prévention, l'incohérence du lecteur et l'initialisation appropriée, TS-Proto recommande d'utiliser useOptionals=none
comme option "la plus sûre".
Cela dit, cette approche oblige les écrivains / créateurs à définir tous les champs (bien que fromPartial
et create
soient destinés à résoudre ce problème), donc si vous souhaitez toujours avoir des clés facultatives, vous pouvez définir useOptionals=messages
ou useOptionals=all
.
(Voir ce problème et ce problème pour les discussions sur useOptional
.)
Empêche les fautes de frappe lors de l'initialisation des messages, et
Fournit l'API la plus cohérente aux lecteurs
Assure que les messages de production sont correctement initialisés avec tous les champs.
Avec --ts_proto_opt=exportCommonSymbols=false
, les types d'utilité comme DeepPartial
et protobufPackage
ne seront pas export
d.
Cela devrait permettre d'utiliser la création d'importations de barils de la sortie générée, c'est-à-dire import * from ./foo
et import * from ./bar
.
Notez que si vous avez le même nom de message utilisé dans plusieurs fichiers *.proto
, vous obtiendrez toujours des conflits d'importation.
Avec --ts_proto_opt=oneof=unions
, oneof
champs seront générés en tant qu'adtes.
Voir la section "Oneof Handling".
Avec --ts_proto_opt=unrecognizedEnumName=<NAME>
Les émous contiendront une clé <NAME>
avec la valeur de l'option unrecognizedEnumValue
.
Par défaut UNRECOGNIZED
.
Avec --ts_proto_opt=unrecognizedEnumValue=<NUMBER>
Les enums contiendront une clé fournie par l'option unrecognizedEnumName
avec la valeur de <NUMBER>
.
Par défaut à -1
.
Avec --ts_proto_opt=unrecognizedEnum=false
Enum ne contiendra pas une clé d'énumération non reconnue et une valeur telle que fournie par les options de unrecognizedEnumName
et unrecognizedEnumValue
.
Avec --ts_proto_opt=removeEnumPrefix=true
généré en énumération aura le nom d'énumération supprimé des membres.
FooBar.FOO_BAR_BAZ = "FOO_BAR_BAZ"
générera FooBar.BAZ = "FOO_BAR_BAZ"
Avec --ts_proto_opt=lowerCaseServiceMethods=true
, les noms de méthodes des méthodes de service seront abaissés / camel-case, c'est-à-dire service.findFoo
au lieu de service.FindFoo
.
Avec --ts_proto_opt=snakeToCamel=false
, les champs seront conservés au cas de serpent dans les clés de message et les méthodes toJSON
/ fromJSON
.
snakeToCamel
peut également être défini sous forme de liste de chaînes dédimités _
(la virgule est réservée comme le drapeau délimité), ie --ts_proto_opt=snakeToCamel=keys_json
, où json
inclusion keys
fera des clés de message cas de chameau.
La chaîne vide, c'est-à-dire snakeToCamel=
, conservera les deux touches de messages et les clés JSON
comme cas de serpent (c'est la même chose que snakeToCamel=false
).
Notez que pour utiliser l'attribut json_name
, vous devrez utiliser le json
.
Le comportement par défaut est keys_json
, c'est-à-dire que les deux seront Camel Based, et json_name
sera utilisé si le défilé.
Avec --ts_proto_opt=outputEncodeMethods=false
, les méthodes Message.encode
et Message.decode
pour travailler avec les données encodées / binaires de protobuf ne seront pas sorties.
Ceci est utile si vous voulez "uniquement des types".
Avec --ts_proto_opt=outputJsonMethods=false
, le Message.fromJSON
et Message.toJSON
Les méthodes de travail avec des données codées par JSON ne seront pas sorties.
Ceci est également utile si vous voulez "uniquement des types".
Avec --ts_proto_opt=outputJsonMethods=to-only
et --ts_proto_opt=outputJsonMethods=from-only
vous pourrez en exporter une seule entre le Message.toJSON
et Message.fromJSON
méthodes.
Ceci est utile si vous utilisez TS-Proto juste pour encode
ou decode
et non pour les deux.
Avec --ts_proto_opt=outputPartialMethods=false
, le Message.fromPartial
et Message.create
des méthodes pour accepter les objets / littéraux d'objets formés partiellement ne seront pas sortis.
Avec --ts_proto_opt=stringEnums=true
, les types d'énumération générés seront basés sur une chaîne au lieu de INT.
Ceci est utile si vous voulez "uniquement des types" et utilisez une passerelle GRPC REST configurée pour sérialiser les énumérations sous forme de chaînes.
(Nécessite outputEncodeMethods=false
.)
Avec --ts_proto_opt=outputClientImpl=false
, les implémentations du client, c'est-à-dire FooServiceClientImpl
, qui implémentent les interfaces RPC côté client (dans Twirp, voir la prochaine option pour grpc-web
) RPC.
Avec --ts_proto_opt=outputClientImpl=grpc-web
, les implémentations du client, c'est-à-dire FooServiceClientImpl
, utiliseront la bibliothèque @ improbable-Eng / Grpc-web à l'exécution pour envoyer des messages Grpc à un backend GRPC-Web.
(Notez que cela n'utilise que l'exécution de GRPC-Web, vous n'avez besoin d'utiliser aucun de leur code généré, c'est-à-dire que la sortie TS-Proto remplace leur sortie ts-protoc-gen
.)
Vous devrez ajouter le @improbable-eng/grpc-web
et un transport vers package.json
de votre projet.json; Voir le répertoire integration/grpc-web
pour un exemple de travail. Voir également # 504 pour l'intégration avec GRPC-Web-Devtools.
Avec --ts_proto_opt=returnObservable=true
, le type de retour des méthodes de service sera Observable<T>
au lieu de Promise<T>
.
Avec --ts_proto_opt=addGrpcMetadata=true
, le dernier argument des méthodes de service acceptera le type Metadata
GRPC, qui contient des informations supplémentaires avec l'appel (c'est-à-dire des jetons d'accès / etc.).
(Nécessite nestJs=true
.)
Avec --ts_proto_opt=addNestjsRestParameter=true
, le dernier argument des méthodes de service sera un paramètre de repos avec type. De cette façon, vous pouvez utiliser des décorateurs personnalisés que vous pouvez normalement utiliser dans NESTJS.
(Nécessite nestJs=true
.)
Avec --ts_proto_opt=nestJs=true
, les valeurs par défaut changeront pour générer des interfaces et des interfaces de service conviviales NESTJS Protobuf qui peuvent être utilisées à la fois dans le côté client et le serveur des implémentations NESTJS Protobuf. Voir le NESTJS Readme pour plus d'informations et des exemples de mise en œuvre.
Spécifiquement outputEncodeMethods
, outputJsonMethods
et outputClientImpl
seront tous faux, lowerCaseServiceMethods
seront vrais et outputServices
seront ignorés.
Notez que addGrpcMetadata
, addNestjsRestParameter
et returnObservable
seront toujours faux.
Avec --ts_proto_opt=useDate=false
, les champs de type google.protobuf.Timestamp
ne seront pas mappés pour taper Date
dans les types générés. Voir l'horodatage pour plus de détails.
Avec --ts_proto_opt=useMongoObjectId=true
, les champs d'un type appelé ObjectId où le message est construit pour avoir sur un champ appelé valeur qui est une chaîne sera mappée pour taper mongodb.ObjectId
dans les types générés. Cela nécessitera votre projet pour installer le package MongoDB NPM. Voir ObjectId pour plus de détails.
Avec --ts_proto_opt=annotateFilesWithVersion=false
, les fichiers générés ne contiendront pas les versions de protoc
et ts-proto
utilisées pour générer le fichier. Cette option est normalement définie sur true
, de sorte que les fichiers répertorient les versions utilisées.
Avec --ts_proto_opt=outputSchema=true
, des méta-dactylographies seront générées qui pourront plus tard être utilisées dans d'autres générateurs de code.
Avec --ts_proto_opt=outputSchema=no-file-descriptor
, les méta-dactylographiques seront générées, mais nous n'incluons pas le descripteur de fichier dans le schéma généré. Ceci est utile si vous essayez de minimiser la taille du schéma généré.
Avec --ts_proto_opt=outputSchema=const
, les méta-dactylographies seront générées as const
, permettant un accès à sécurité de type à toutes ses propriétés. (Fonctionne uniquement avec TypeScript 4.9 et plus, car il utilise également l'opérateur satisfies
). Peut être combiné avec l'option no-file-descriptor
( outputSchema=const,outputSchema=no-file-descriptor
) pour ne pas inclure le descripteur de fichier dans le schéma généré.
Avec --ts_proto_opt=outputTypeAnnotations=true
, chaque message recevra un champ $type
contenant son nom entièrement qualifié. Vous pouvez utiliser --ts_proto_opt=outputTypeAnnotations=static-only
pour l'omettre à partir de la déclaration interface
, ou --ts_proto_opt=outputTypeAnnotations=optional
pour en faire une propriété facultative sur la définition interface
. Cette dernière option peut être utile si vous souhaitez utiliser le champ $type
pour la vérification des types d'exécution sur les réponses d'un serveur.
Avec --ts_proto_opt=outputTypeRegistry=true
, le registre de type sera généré qui peut être utilisé pour résoudre les types de messages par nom entièrement qualifié. De plus, chaque message recevra un champ $type
contenant son nom entièrement qualifié.
Avec --ts_proto_opt=outputServices=grpc-js
, TS-Proto publiera les définitions de service et les stubs Server / Client au format Grpc-JS.
Avec --ts_proto_opt=outputServices=generic-definitions
, TS-Proto sortira des définitions de services génériques (framework-agnostique). Ces définitions contiennent des descripteurs pour chaque méthode avec des liens aux types de demande et de réponse, ce qui permet de générer des talons de serveur et client lors de l'exécution, et de générer également des types solides pour eux au moment de la compilation. Un exemple de bibliothèque qui utilise cette approche est Nice-GRPC.
Avec --ts_proto_opt=outputServices=nice-grpc
, TS-Proto publiera le serveur et les talons clients pour Nice-Grpc. Cela doit être utilisé avec des définitions génériques, c'est-à-dire que vous devez spécifier deux options: outputServices=nice-grpc,outputServices=generic-definitions
.
Avec --ts_proto_opt=metadataType=Foo@./some-file
, ts-proto ajouter un champ de métadonnées générique (framework-agnostique) à la définition générique de service.
Avec --ts_proto_opt=outputServices=generic-definitions,outputServices=default
, TS-Proto sortira à la fois des définitions et des interfaces génériques. Ceci est utile si vous souhaitez compter sur les interfaces, mais que vous avez également quelques capacités de réflexion au moment de l'exécution.
Avec --ts_proto_opt=outputServices=false
, OR =none
, TS-Proto ne sortira aucune définitions de service.
Avec --ts_proto_opt=rpcBeforeRequest=true
, ts-proto ajoutera une définition de fonction à la définition de l'interface RPC avec la signature: beforeRequest(service: string, message: string, request: <RequestType>)
. Il définira également automatiquement outputServices=default
. Chacune des méthodes du service appellera beforeRequest
avant d'effectuer sa demande.
Avec --ts_proto_opt=rpcAfterResponse=true
, TS-Proto ajoutera une définition de fonction à la définition de l'interface RPC avec la signature: afterResponse(service: string, message: string, response: <ResponseType>)
. Il définira également automatiquement outputServices=default
. Chacune des méthodes du service appellera afterResponse
avant de retourner la réponse.
Avec --ts_proto_opt=rpcErrorHandler=true
, TS-Proto ajoutera une définition de fonction à la définition de l'interface RPC avec la signature: handleError(service: string, message: string, error: Error)
. Il définira également automatiquement outputServices=default
.
Avec --ts_proto_opt=useAbortSignal=true
, les services générés accepteront un AbortSignal
pour annuler les appels RPC.
Avec --ts_proto_opt=useAsyncIterable=true
, les services générés utiliseront AsyncIterable
au lieu Observable
.
Avec --ts_proto_opt=emitImportedFiles=false
, TS-Proto n'émettra pas les fichiers google/protobuf/*
à moins que vous ne compliciez expliciter des fichiers à protoc
comme ce protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto my_message.proto google/protobuf/duration.proto
Avec --ts_proto_opt=fileSuffix=<SUFFIX>
, TS-Proto émettra des fichiers générés à l'aide du suffixe spécifié. Un fichier helloworld.proto
avec fileSuffix=.pb
serait généré comme helloworld.pb.ts
. Il s'agit d'un comportement commun dans d'autres plugins Protoc et fournit un moyen de globaliser rapidement tous les fichiers générés.
Avec --ts_proto_opt=importSuffix=<SUFFIX>
, TS-Proto émettra les importations de fichiers à l'aide du suffixe spécifié. Une importation de helloworld.ts
avec fileSuffix=.js
générerait import "helloworld.js"
. La valeur par défaut est d'importer sans extension de fichier. Prise en charge par TypeScript 4.7.x et UP.
Avec --ts_proto_opt=enumsAsLiterals=true
, les types d'énumération générés seront un objet Enum-ish avec as const
.
Avec --ts_proto_opt=useExactTypes=false
, les méthodes générées fromPartial
et create
n'utiliseront pas de types exacts.
Le comportement par défaut est useExactTypes=true
, qui fabrique fromPartial
et create
d'utiliser un type exact pour son argument pour que TypeScript rejette toutes les propriétés inconnues.
Avec --ts_proto_opt=unknownFields=true
, tous les champs inconnus seront analysés et sorties sous forme de tableaux de tampons.
Avec --ts_proto_opt=onlyTypes=true
, seuls les types seront émis et les importations pour long
et protobufjs/minimal
seront exclues.
Ceci est le même que le réglage de outputJsonMethods=false,outputEncodeMethods=false,outputClientImpl=false,nestJs=false
Avec --ts_proto_opt=usePrototypeForDefaults=true
, le code généré enveloppera de nouveaux objets avec Object.create
.
Cela permet au code de faire des contrôles Hazzer pour détecter lorsque des valeurs par défaut ont été appliquées, ce qui en raison du comportement de Proto3 de ne pas mettre de valeurs par défaut sur le fil, n'est généralement utile que pour interagir avec les messages Proto2.
Lorsqu'ils sont activés, les valeurs par défaut sont héritées d'un prototype, et le code peut donc utiliser object.keys (). Inclut ("Somefield") pour détecter si certains champs ont été réellement décodés ou non.
Notez que, comme indiqué, cela signifie Object. Les clés n'incluront pas les champs définis par défaut, donc si vous avez du code qui itère sur les clés des messages de manière générique, il devra également itérater sur les clés héritées du prototype.
Avec --ts_proto_opt=useJsonName=true
, json_name
défini dans les protofiles sera utilisé à la place des noms de champ de message.
Avec --ts_proto_opt=useJsonWireFormat=true
, le code généré reflétera la représentation JSON des messages Protobuf.
Nécessite onlyTypes=true
. Implique useDate=string
et stringEnums=true
. Cette option consiste à générer des types qui peuvent être directement utilisés avec les messages protobuf de maréchalage / unmarshalling sérialisés en JSON. Vous pouvez également définir useOptionals=all
, car les passerelles GRPC ne sont pas nécessaires pour envoyer une valeur par défaut pour les valeurs scalaires.
Avec --ts_proto_opt=useNumericEnumForJson=true
, le convertisseur JSON ( toJSON
) codera les valeurs d'énumération comme int, plutôt qu'un littéral de chaîne.
Avec --ts_proto_opt=initializeFieldsAsUndefined=false
, tous les initialiseurs de champ facultatifs seront omis des instances de base générées.
Avec --ts_proto_opt=disableProto2Optionals=true
, tous les champs facultatifs sur les fichiers proto2 ne seront pas définis pour être facultatifs. Veuillez noter que ce drapeau est principalement pour la préservation de la gestion de l'héritage de TS-Proto des fichiers Proto2, pour éviter de casser les modifications et, par conséquent, il n'est pas destiné à être utilisé à l'avenir.
Avec --ts_proto_opt=disableProto2DefaultValues=true
, tous les champs des fichiers proto2 qui spécifient une valeur par défaut n'utiliseront pas réellement cette valeur par défaut. Veuillez noter que ce drapeau est principalement pour la préservation de la gestion de l'héritage de TS-Proto des fichiers Proto2, pour éviter de casser les modifications et, par conséquent, il n'est pas destiné à être utilisé à l'avenir.
Avec --ts_proto_opt=Mgoogle/protobuf/empty.proto=./google3/protobuf/empty
, ('m' signifie 'importmapping', similaire à protoc-gen-go), le chemin d'importation de code généré pour ./google/protobuf/empty.ts
reflétera la valeur remplacée:
Mfoo/bar.proto=@myorg/some-lib
mrolèons les importations foo/bar.proto
dans import ... from '@myorg/some-lib'
.
Mfoo/bar.proto=./some/local/lib
mrolèons les importations foo/bar.proto
dans import ... from './some/local/lib'
.
Mfoo/bar.proto=some-modules/some-lib
cartographier les importations foo/bar.proto
dans import ... from 'some-module/some-lib'
.
Remarque : les utilisations sont accumulées, donc plusieurs valeurs sont attendues sous la forme de --ts_proto_opt=M... --ts_proto_opt=M...
(un ts_proto_opt
par mappage).
Remarque : les fichiers Proto qui correspondent aux importations mappées ne seront pas générées .
Avec --ts_proto_opt=useMapType=true
, le code généré pour map<key_type, value_type>
deviendra Map<key_type, value_type>
qui utilise le type de carte javascript.
Le comportement par défaut est useMapType=false
, ce qui le fait générer le code pour map<key_type, value_type
avec la paire de valeurs de clé comme {[key: key_type]: value_type}
.
Avec --ts_proto_opt=useReadonlyTypes=true
, les types générés seront déclarés immuables à l'aide du modificateur readonly
de TypeScript.
Avec --ts_proto_opt=useSnakeTypeName=false
supprimera le boîtier de serpent des types.
Exemple de protobuf
Message Box {Message Element {Message Image {Enum Alignment {Left = 1; Centre = 2; À droite = 3; } } } }
Par défaut, cela est activé qui générerait un type de Box_Element_Image_Alignment
. En désactivant cette option, le type généré serait BoxElementImageAlignment
.
Avec --ts_proto_opt=outputExtensions=true
, le code généré comprendra des extensions de proto2
Les méthodes d'extension Encode / Decode sont conformes à l'option outputEncodeMethods
, et si unknownFields=true
, les méthodes setExtension
et getExtension
seront créées pour des messages extensibles, également conformes à outputEncodeMethods
(settension = Encode, getExtension = Decode).
Avec --ts_proto_opt=outputIndex=true
, les fichiers index seront générés en fonction des espaces de noms Proto Package.
Cela désactivera exportCommonSymbols
pour éviter les collisions de noms sur les symboles communs.
Avec --ts_proto_opt=emitDefaultValues=json-methods
, la méthode TOJSON générée émetra des scalaires comme 0
et ""
comme champs JSON.
Avec --ts_proto_opt=comments=false
, les commentaires ne seront pas copiés à partir des fichiers Proto au code généré.
Avec --ts_proto_opt=bigIntLiteral=false
, le code généré utilisera BigInt("0")
au lieu de 0n
pour les littéraux BigInt. Les littéraux BigInt ne sont pas pris en charge par TypeScript lorsque l'option de compilateur "cible" définit quelque chose de plus ancien que "ES2020".
Avec --ts_proto_opt=useNullAsOptional=true
, les valeurs undefined
seront converties en null
, et si vous utilisez une étiquette optional
dans votre fichier .proto
, le champ aura également un type undefined
. Par exemple:
Avec --ts_proto_opt=typePrefix=MyPrefix
, les interfaces générées, les énuméraires et les usines auront un préfixe de MyPrefix
dans leurs noms.
Avec --ts_proto_opt=typeSuffix=MySuffix
, les interfaces, les énuméraires et les usines générés auront un suffixe de MySuffix
dans leurs noms.
Message ProfileInfo {int32 id = 1; String bio = 2; String Phone = 3; } Message Department {int32 id = 1; String name = 2; } Message User {int32 id = 1; String username = 2; / * ProfileInfo sera facultatif dans TypeScript, le type sera ProfileInfo | NULL | INDÉFINÉ CECI est nécessaire dans les cas où vous ne voulez pas fournir de valeur pour le profil. * / Profil facultatif ProfileInfo Profil = 3; / * Le département accepte uniquement un type de département ou NULL, ce qui signifie que vous devez le passer null s'il n'y a pas de valeur disponible. * / Département du département = 4; }
Les interfaces générées seront:
Exporter Profile InterfaceInfo { ID: numéro; bio: chaîne; Téléphone: chaîne;} Département d'interface d'exportation { ID: numéro; Nom: String;} Interface d'exportation User { ID: numéro; Nom d'utilisateur: String; Profil ?: ProfilInfo | NULL | indéfini; // Vérifiez celui-ci Département: Département | nul; // Vérifiez celui-ci}
Avec --ts_proto_opt=noDefaultsForOptionals=true
, les valeurs primitives undefined
ne seront pas par défaut selon la spécification Protobuf. De plus, contrairement au comportement standard, lorsqu'un champ est défini sur sa valeur par défaut standard, il sera codé pour lui permettre d'être envoyé sur le fil et distingué des valeurs non définies. Par exemple, si un message ne définit pas de valeur booléenne, généralement cela serait par défaut à false
ce qui est différent de son non-défini.
Cette option permet à la bibliothèque d'agir de manière compatible avec l'implémentation de fil maintenue et utilisée par Square / Block. Remarque: Cette option ne doit être utilisée qu'en combinaison avec un autre code client / serveur généré à l'aide de Wire ou TS-Proto avec cette option activée.
Nous avons une excellente façon de travailler avec NESTJS. ts-proto
génère interfaces
et decorators
pour votre contrôleur, client. Pour plus d'informations, consultez le NESTJS Readme.
Si vous souhaitez exécuter ts-proto
sur chaque modification d'un fichier Proto, vous devrez utiliser un outil comme Chokidar-Cli et l'utiliser comme script dans package.json
:
"proto: générer": "protoC --ts_proto_out = ./ <proto_path>/<proto_name>.proto --ts_proto_opt = esmoduleInterop = true", "proto: watch": "chokidar" ** / *. proto "- C "NPM Run Proto: Générer" "
ts-proto
est RPC Framework Agnostic - la façon dont vous transmettez vos données à et depuis votre source de données dépend de vous. Les implémentations clients générées s'attendent à un paramètre rpc
, quel type est défini comme ceci:
interface rpc { demande (service: chaîne, méthode: chaîne, données: uint8array): promesse <uint8Array>;}
Si vous travaillez avec GRPC, une simple implémentation pourrait ressembler à ceci:
const Conn = new grpc.client ( "LocalHost: 8765", Grpc.Credentials.CreateInSecure ()); Type RPCIMPL = (Service: String, Method: String, Data: Uint8Array) => Promise <Uint8Array>; const SendRequest: RPCIMPL = (Service, Method, Data) => { // Conventionnellement dans GRPC, le chemin de demande ressemble à // "pack // Nous construisons donc une telle chaîne const path = `/ $ {service} / $ {méthode}`; renvoyer une nouvelle promesse ((résoudre, rejeter) => {// MakeUnaryRequest transmet le résultat (et l'erreur) avec un rappel // transforment cela en une promesse! const Resultialback: unaryCallback <yy> = (err, res) => {si (err) {return reject (err);} résolve (res);}; fonction passhrough (argument: any) {return argument;} // en utilisant le passhrough comme sérialisation et désérialiser FunctionSonn , resultCallback); });}; const rpc: rpc = {request: sendRequest};
Félicitations à nos sponsors:
NGROK a financé le support initial du GRPC-Web de TS-Proto.
Si vous avez besoin de personnalisations TS-Proto ou de prise en charge prioritaire pour votre entreprise, vous pouvez me faire un ping à travers par e-mail.
Cette section décrit comment contribuer directement à TS-Proto, c'est-à-dire qu'elle n'est pas nécessaire pour exécuter ts-proto
en protoc
ou en utilisant le type de type généré.
Exigences
Docker
yarn
- npm install -g yarn
Installation
Les commandes ci-dessous supposent que Docker a installé. Si vous utilisez OS X, installez CoreUtils , brew install coreutils
.
Consultez le référentiel du dernier code.
Exécutez yarn install
pour installer les dépendances.
Exécutez yarn build:test
pour générer les fichiers de test.
Cela exécute les commandes suivantes:
proto2ts
- Exécute ts-proto
sur les fichiers integration/**/*.proto
pour générer des fichiers .ts
.
proto2pbjs
- génère une implémentation de référence à l'aide pbjs
pour tester la compatibilité.
Exécuter yarn test
Flux de travail
Ajouter / mettre à jour un test d'intégration pour votre cas d'utilisation
Vous pouvez également laisser yarn watch
en marche, et cela devrait "faire la bonne chose"
Faites une nouvelle integration/your-new-test/parameters.txt
avec les paramètres ts_proto_opt
nécessaires
Créez une integration/your-new-test/your-new-test.proto
schéma pour reproduire votre cas d'utilisation
Soit trouver une integration/*
test existant qui est suffisamment proche de votre cas d'utilisation, par exemple a un parameters.txt
qui correspond aux paramètres ts_proto_opt
nécessaires pour reproduire votre cas d'utilisation
Si vous créez un nouveau test d'intégration:
Après toute modification de your-new-test.proto
, ou une intégration existante yarn proto2bin
integration/*.proto
Ajouter / mettre à jour une integration/your-new-test/some-test.ts
Test de l'unité, même si elle est aussi triviale que de s'assurer que le code généré compile
Modifiez la logique de génération de code ts-proto
:
Ou yarn proto2ts your-new-test
à re-codéger un test spécifique
Encore une fois, laisser yarn watch
devrait "faire la bonne chose"
La logique la plus importante se trouve dans src / main.ts.
Après toute modification des fichiers src/*.ts
, exécutez yarn proto2ts
pour redégenter tous les tests d'intégration
Exécutez yarn test
pour vérifier vos modifications passez tous les tests existants
Commettre et soumettre un PR
Parfois, la vérification du code généré est mal vu, mais étant donné le travail principal de TS-Proto est de générer du code, voir le Codegen diffs dans PRS est utile
Exécutez yarn format
pour formater les fichiers TypeScript.
Assurez-vous git add
tous les fichiers *.proto
, *.bin
et *.ts
dans integration/your-new-test
Tester dans vos projets
Vous pouvez tester vos changements TS-Proto locaux dans vos propres projets en exécutant yarn add ts-proto@./path/to/ts-proto
, tant que vous exécutez yarn build
manuellement.
Protoc docké
Le référentiel comprend une version dockée de protoc
, qui est configurée dans docker-compose.yml.
Il peut être utile au cas où vous souhaitez invoquer manuellement le plugin avec une version connue de protoc
.
Usage:
# Incluez l'alias Protoc dans votre coquille .. alias.sh # Exécutez le protoC comme d'habitude. Le répertoire TS-Proto est disponible dans /ts-proto.protoc --plugin = / ts-proto / protoc-gen-ts_proto --ts_proto_out =. / Output -i =. / Protos ./protoc/*.proto# ou Utilisez l'alias TS-Protoc qui spécifie le chemin du plugin pour vous.
Tous les chemins doivent être des chemins relatifs dans le répertoire de travail actuel de l'hôte. ../
n'est pas autorisé
Dans le conteneur Docker, le chemin absolu vers la racine du projet est /ts-proto
Le conteneur monte le répertoire de travail actuel dans /host
et le définit comme son répertoire de travail.
Une fois aliases.sh
provenant, vous pouvez utiliser la commande protoc
dans n'importe quel dossier.
Le nom du module TS / ES6 est le package Proto
Soutenez le codage basé sur les chaînes de la durée dans fromJSON
/ toJSON
Faire oneof=unions-value
le comportement par défaut en 2.0
Changer probablement par défaut forceLong
en 2.0, devrait par défaut sur forceLong=long
Faire esModuleInterop=true
la valeur par défaut en 2.0
Par défaut, TS-Proto modélise oneof
des champs "catégoriquement" dans le message, par exemple un message comme:
Message foo {oneof onef_field {String field_a = 1; String field_b = 2; } }
Génera un type Foo
avec deux champs: field_a: string | undefined;
et field_b: string | undefined
.
Avec cette sortie, vous devrez vérifier à la fois if object.field_a
et if object.field_b
, et si vous en définissez un, vous devrez vous soucier de ne pas set l'autre.
Au lieu de cela, nous vous recommandons d'utiliser l'option oneof=unions-value
, qui changera la sortie pour être un type de données algébrique / ADT comme:
Interface YourMessage { SoitField ?: {$ case: "field_a"; Valeur: String} | {$ case: "field_b"; valeur: chaîne};}
Comme cela n'appliquera automatiquement qu'un seul de field_a
ou field_b
"étant défini" à la fois, car les valeurs sont stockées dans le champ eitherField
qui ne peut avoir qu'une seule valeur à la fois.
(Notez que eitherField
est facultatif B / C oneof
en protobuf signifie "dans le plus un champ" est défini et ne signifie pas qu'un des champs doit être défini.)
Dans la version 2.x actuellement non réculée de TS-Proto, oneof=unions-value
deviendra le comportement par défaut.
Il existe également une option oneof=unions
, qui génère une union où les noms de champ sont inclus dans chaque option:
Interface YourMessage { SoitField ?: {$ case: "field_a"; field_a: String} | {$ case: "field_b"; field_b: chaîne};}
Ceci n'est plus recommandé car il peut être difficile d'écrire du code et des types pour gérer plusieurs options ONE:
Les types d'assistance suivants peuvent faciliter le travail avec les types générés à partir de oneof=unions
, bien qu'ils ne soient généralement pas nécessaires si vous utilisez oneof=unions-value
:
/ ** extrait tous les noms de cas d'un champ Oneof. * / Tapez oneofcases <T> = T étend {$ case: inférer u étend la chaîne}? U: jamais; / ** extrait une union de tous les types de valeur à partir d'un champ Oneof * / type oneofvalues <T> = T étend {$ case: inférer u étend la chaîne; [clé: chaîne]: inconnu}? T [u]: jamais; / ** extrait le type spécifique d'un cas en fonction de son nom de champ * / type oneofcase <t, k étend unofcases <T>> = T étend { $ cas: k; [clé: chaîne]: inconnu;} ? T : jamais; / ** extrait le type spécifique d'un type de valeur à partir d'un champ Oneof * / type oneofvalue <t, k étend unofcases <T>> = T étend { $ cas: inférer u étend k; [clé: chaîne]: inconnu;} ? T [u] : jamais;
À titre de comparaison, les équivalents pour oneof=unions-value
:
/ ** extrait tous les noms de cas d'un champ Oneof. * / type oneofcases <t> = t ['$ case']; / ** extrait une union de tous les types de valeur à partir d'un champ * / type oneofvalues <T> = t ['value']; / ** extraits Le type spécifique d'un cas unique basé sur son nom de champ * / type oneofcase <t, k étend unofcases <T>> = T étend { $ cas: k; [clé: chaîne]: inconnu;} ? T : jamais; / ** extrait le type spécifique d'un type de valeur à partir d'un champ Oneof * / type oneofvalue <t, k étend unofcases <T>> = T étend { $ cas: inférer u étend k; Valeur: inconnue;} ? T [u] : jamais;
Dans Core Protobuf (et donc aussi ts-proto
), les valeurs non définies ou égales à la valeur par défaut ne sont pas envoyées sur le fil.
Par exemple, la valeur par défaut d'un message undefined
. Les types primitifs prennent leur valeur par défaut naturelle, string
par exemple est ''
, number
est 0
, etc.
Protobuf a choisi / applique ce comportement car il permet une compatibilité directe, car les champs primitifs auront toujours une valeur, même lorsqu'ils sont omis par des agents obsolètes.
C'est bien, mais cela signifie également que les valeurs par défaut et non set ne peuvent pas être distinguées dans les champs ts-proto
; C'est juste fondamentalement comment fonctionne Protobuf.
Si vous avez besoin de champs primitifs où vous pouvez détecter le jeu / unset, consultez les types de wrapper.
Encoder / Decode
ts-proto
suit les règles Protobuf et renvoie toujours des valeurs par défaut pour les champs unis lors du décodage, tout en les omettant de la sortie lorsqu'il est sérialisé au format binaire.
syntax = "proto3"; message foo {String bar = 1; }
Protobufbytes; // Supposons qu'il s'agit d'un objet FOO vide, dans Protobuf Binary FormatFoo.decode (ProtobufBytes); // => {bar: ''}
Foo.encode ({bar: ""}); // => {}, écrit un objet FOO vide, au format binaire Protobuf
Fromjson / tojson
La lecture de JSON initialise également les valeurs par défaut. Étant donné que les expéditeurs peuvent omettre les champs non défini ou les définir sur la valeur par défaut, utilisez fromJSON
pour normaliser l'entrée.
Foo.fromjson ({}); // => {bar: ''} foo.fromjson ({bar: ""}); // => {bar: ''} foo.fromjson ({bar: "baz"}); // => {bar: 'baz'}
Lors de l'écriture de JSON, ts-proto
normalise les messages en omettant des champs et des champs unis définis sur leurs valeurs par défaut.
Foo.tojson ({}); // => {} foo.tojson ({bar: undefined}); // => {} foo.tojson ({bar: ""}); // => {} - Remarque: omettant la valeur par défaut, comme attendufoo.tojson ({bar: "baz"}); // => {bar: 'baz'}
Protobuf est livré avec plusieurs définitions de messages prédéfinis, appelés "types bien connus". Leur interprétation est définie par la spécification Protobuf, et les bibliothèques devraient convertir ces messages en types natifs correspondants dans la langue cible.
ts-proto
convertit actuellement ces messages en leurs types natifs correspondants.
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
(ie number | string | boolean | null | array | object
)
google.protobuf.struct ⇆ { [key: string]: any }
Les types de wrapper sont des messages contenant un seul champ primitif et peuvent être importés dans des fichiers .proto
avec import "google/protobuf/wrappers.proto"
.
Étant donné que ce sont des messages , leur valeur par défaut n'est undefined
, vous permettant de distinguer les primitives unvées de leurs valeurs par défaut, lors de l'utilisation de types de wrapper. ts-proto
génère ces champs comme <primitive> | undefined
.
Par exemple:
// protobufsyntax = "proto3"; import "google / protobuf / wrappers.proto"; message exampleMessage {google.protobuf.stringValue name = 1; }
// TypeScriptInterface exampleMessage { Nom: String | indéfini;}
Lors du codage d'un message, la valeur primitive est convertie à son type de wrapper correspondant:
ExampleMessage.encode ({name: "foo"}); // => {nom: {valeur: 'foo'}}, en binaire
Lorsque vous appelez TOJSON, la valeur n'est pas convertie, car les types de wrapper sont idiomatiques dans JSON.
ExampleMessage.tojson ({name: "foo"}); // => {nom: 'foo'}
La langue et les types de Protobuf ne sont pas suffisants pour représenter toutes les valeurs JSON possibles, car JSON peut contenir des valeurs dont le type est inconnu à l'avance. Pour cette raison, Protobuf propose plusieurs types supplémentaires pour représenter des valeurs JSON arbitraires.
Ceux-ci sont appelés types de structure et peuvent être importés dans des fichiers .proto
avec import "google/protobuf/struct.proto"
.
google.protobuf.value any
Il s'agit du type le plus général et peut représenter n'importe quelle valeur JSON (IE number | string | boolean | null | array | object
).
google.protobuf.listValue ⇆ any[]
Pour représenter un tableau JSON
google.protobuf.struct ⇆ { [key: string]: any }
Pour représenter un objet JSON
ts-proto
se convertit automatiquement entre ces types de structure et leurs types JSON correspondants.
Exemple:
// protobufsyntax = "proto3"; import "google / protobuf / struct.proto"; message exampleMessage {google.protobuf.value tout = 1; }
// TypeScriptInterface exampleMessage { Tout: tout | indéfini;}
Encodage d'une valeur JSON intégrée dans un message, la convertit en un type de structure:
ExampleMessage.encode ({n'importe quoi: {name: "Hello"}}); / * produit la structure suivante, codée en format binaire Protobuf: {n'importe quoi: valeur {structValue = struct {fields = [mAPEntry {key = "name", Value = Value {StringValue = "Hello"}]}}}} * / exampleMessage.encode ({n'importe quoi: true}); / * produit la structure suivante codée dans Protobuf Binary Format: {tout: valeur {boolValue = true}} * /
La représentation de google.protobuf.Timestamp
est configurable par l'indicateur useDate
. Le drapeau useJsonTimestamp
contrôle la précision lorsqu'il est useDate
est false
.
Type bien connu Protobuf | Par défaut / useDate=true | useDate=false | useDate=string | useDate=string-nano |
---|---|---|---|---|
google.protobuf.Timestamp | Date | { seconds: number, nanos: number } | string | string |
Lorsque vous utilisez useDate=false
et useJsonTimestamp=raw
est représenté comme { seconds: number, nanos: number }
, mais a une précision nanoseconde.
Lorsque vous utilisez useDate=string-nano
Timestamp est représenté comme une chaîne ISO avec Nanoseconde Precision 1970-01-01T14:27:59.987654321Z
et s'appuie sur la bibliothèque nano-date pour la conversion. Vous devrez l'installer dans votre projet.
Les numéros sont par défaut supposés être number
JavaScript simples s.
C'est bien pour les types de protobuf comme int32
et float
, mais les types 64 bits comme int64
ne peuvent pas être représentés à 100% par le type number
de JavaScript, car int64
peut avoir des valeurs plus grandes / plus petites que number
.
La configuration par défaut de TS-Proto (qui est forceLong=number
) est d'utiliser toujours number
pour les champs 64 bits, puis de lancer une erreur si une valeur (au moment de l'exécution) est supérieure à Number.MAX_SAFE_INTEGER
.
Si vous vous attendez à utiliser des valeurs 64 bits / supérieures à MAX_SAFE_INTEGER
, vous pouvez utiliser l'option TS-Proto forceLong
, qui utilise le package NPM long pour prendre en charge toute la gamme de valeurs 64 bits.
Les types de nombres Protobuf sont des types JavaScript en fonction de l'option de configuration forceLong
:
Types de nombres Protobuf | Par défaut / forceLong=number | forceLong=long | forceLong=string |
---|---|---|---|
double | nombre | nombre | nombre |
flotter | nombre | nombre | nombre |
int32 | nombre | nombre | nombre |
int64 | nombre* | Long | chaîne |
uint32 | nombre | nombre | nombre |
uint64 | nombre* | Non signé | chaîne |
sint32 | nombre | nombre | nombre |
sint64 | nombre* | Long | chaîne |
fixe32 | nombre | nombre | nombre |
fixe64 | nombre* | Non signé | chaîne |
sfixed32 | nombre | nombre | nombre |
sfixed64 | nombre* | Long | chaîne |
Où (*) indique qu'ils pourraient lancer une erreur au moment de l'exécution.
Primitives requises: Utilisez en tant qu'ensemble, c'est-à-dire string name = 1
.
Primitives facultatives: utilisez des types de wrapper, IE StringValue name = 1
.
Messages requis: non disponible
Messages facultatifs: utilisez en tant que SubMessage message = 1
.