From 7f462519bb030e876c9e72f52882426cf700ce4b Mon Sep 17 00:00:00 2001 From: philikon Date: Sat, 30 May 2020 01:26:29 -0700 Subject: [PATCH] Compiler flag nullableOptionals=true turns message + oneof fields into optional and nullable properties --- README.markdown | 2 + .../simple-long-string/import_dir/thing.ts | 2 +- integration/simple-long-string/simple.ts | 2 +- integration/simple-long/import_dir/thing.ts | 2 +- integration/simple-long/simple.ts | 2 +- .../google/protobuf/timestamp.ts | 190 ++ .../google/protobuf/wrappers.bin | Bin 0 -> 4316 bytes .../google/protobuf/wrappers.proto | 118 ++ .../google/protobuf/wrappers.ts | 609 ++++++ .../import_dir/thing.bin | Bin 0 -> 6408 bytes .../import_dir/thing.proto | 7 + .../import_dir/thing.ts | 92 + .../nullable-optionals-test.ts | 20 + .../simple-nullable-optionals/parameters.txt | 1 + .../simple-nullable-optionals/simple.bin | Bin 0 -> 17013 bytes .../simple-nullable-optionals/simple.proto | 121 ++ .../simple-nullable-optionals/simple.ts | 1854 +++++++++++++++++ .../simple-nullable-optionals/thing.bin | Bin 0 -> 6386 bytes .../simple-nullable-optionals/thing.proto | 7 + .../simple-nullable-optionals/thing.ts | 92 + .../simple-snake/google/protobuf/wrappers.ts | 2 +- integration/simple-snake/import_dir/thing.ts | 2 +- integration/simple-snake/simple.ts | 2 +- .../simple/google/protobuf/wrappers.ts | 2 +- integration/simple/import_dir/thing.ts | 2 +- integration/simple/simple.ts | 4 +- src/main.ts | 35 +- src/types.ts | 56 +- src/utils.ts | 4 + 29 files changed, 3198 insertions(+), 32 deletions(-) create mode 100644 integration/simple-nullable-optionals/google/protobuf/timestamp.ts create mode 100644 integration/simple-nullable-optionals/google/protobuf/wrappers.bin create mode 100644 integration/simple-nullable-optionals/google/protobuf/wrappers.proto create mode 100644 integration/simple-nullable-optionals/google/protobuf/wrappers.ts create mode 100644 integration/simple-nullable-optionals/import_dir/thing.bin create mode 100644 integration/simple-nullable-optionals/import_dir/thing.proto create mode 100644 integration/simple-nullable-optionals/import_dir/thing.ts create mode 100644 integration/simple-nullable-optionals/nullable-optionals-test.ts create mode 100644 integration/simple-nullable-optionals/parameters.txt create mode 100644 integration/simple-nullable-optionals/simple.bin create mode 100644 integration/simple-nullable-optionals/simple.proto create mode 100644 integration/simple-nullable-optionals/simple.ts create mode 100644 integration/simple-nullable-optionals/thing.bin create mode 100644 integration/simple-nullable-optionals/thing.proto create mode 100644 integration/simple-nullable-optionals/thing.ts diff --git a/README.markdown b/README.markdown index a671ef8a8..77ce10604 100644 --- a/README.markdown +++ b/README.markdown @@ -213,6 +213,8 @@ protoc --plugin=node_modules/ts-proto/protoc-gen-ts_proto ./batching.proto -I. Alternatively, if you pass `--ts_proto_opt=forceLong=string`, all 64 bit numbers will be outputted as strings. +* With `--ts_proto_opt=nullableOptionals=true`, all fields that contain a message or are part of a `oneof` clause are declared as optional TypeScript properties and are marked nullable. + * With `--ts_proto_opt=lowerCaseServiceMethods=true`, the method names of service methods will be lowered/camel-case, i.e. `service.findFoo` instead of `service.FindFoo`. * With `--ts_proto_opt=outputEncodeMethods=false`, the `Message.encode` and `Message.decode` methods for working with protobuf-encoded/binary data will not be output. diff --git a/integration/simple-long-string/import_dir/thing.ts b/integration/simple-long-string/import_dir/thing.ts index dc1989cfe..d3d9d72f4 100644 --- a/integration/simple-long-string/import_dir/thing.ts +++ b/integration/simple-long-string/import_dir/thing.ts @@ -76,7 +76,7 @@ export const ImportedThing = { }, toJSON(message: ImportedThing): unknown { const obj: any = {}; - obj.createdAt = message.createdAt !== undefined ? message.createdAt.toISOString() : null; + obj.createdAt = (message.createdAt !== undefined && message.createdAt !== null) ? message.createdAt.toISOString() : null; return obj; }, }; diff --git a/integration/simple-long-string/simple.ts b/integration/simple-long-string/simple.ts index 81645de4a..84765a53c 100644 --- a/integration/simple-long-string/simple.ts +++ b/integration/simple-long-string/simple.ts @@ -610,7 +610,7 @@ export const Simple = { const obj: any = {}; obj.name = message.name || ""; obj.age = message.age || 0; - obj.createdAt = message.createdAt !== undefined ? message.createdAt.toISOString() : null; + obj.createdAt = (message.createdAt !== undefined && message.createdAt !== null) ? message.createdAt.toISOString() : null; obj.child = message.child ? Child.toJSON(message.child) : undefined; obj.state = StateEnum.toJSON(message.state); if (message.grandChildren) { diff --git a/integration/simple-long/import_dir/thing.ts b/integration/simple-long/import_dir/thing.ts index e7d02c86a..0885f52a8 100644 --- a/integration/simple-long/import_dir/thing.ts +++ b/integration/simple-long/import_dir/thing.ts @@ -81,7 +81,7 @@ export const ImportedThing = { }, toJSON(message: ImportedThing): unknown { const obj: any = {}; - obj.createdAt = message.createdAt !== undefined ? message.createdAt.toISOString() : null; + obj.createdAt = (message.createdAt !== undefined && message.createdAt !== null) ? message.createdAt.toISOString() : null; return obj; }, }; diff --git a/integration/simple-long/simple.ts b/integration/simple-long/simple.ts index 20b84a7bd..483296bc9 100644 --- a/integration/simple-long/simple.ts +++ b/integration/simple-long/simple.ts @@ -610,7 +610,7 @@ export const Simple = { const obj: any = {}; obj.name = message.name || ""; obj.age = message.age || 0; - obj.createdAt = message.createdAt !== undefined ? message.createdAt.toISOString() : null; + obj.createdAt = (message.createdAt !== undefined && message.createdAt !== null) ? message.createdAt.toISOString() : null; obj.child = message.child ? Child.toJSON(message.child) : undefined; obj.state = StateEnum.toJSON(message.state); if (message.grandChildren) { diff --git a/integration/simple-nullable-optionals/google/protobuf/timestamp.ts b/integration/simple-nullable-optionals/google/protobuf/timestamp.ts new file mode 100644 index 000000000..f9591556d --- /dev/null +++ b/integration/simple-nullable-optionals/google/protobuf/timestamp.ts @@ -0,0 +1,190 @@ +import * as Long from 'long'; +import { Writer, Reader } from 'protobufjs/minimal'; + + +/** + * A Timestamp represents a point in time independent of any time zone or local + * calendar, encoded as a count of seconds and fractions of seconds at + * nanosecond resolution. The count is relative to an epoch at UTC midnight on + * January 1, 1970, in the proleptic Gregorian calendar which extends the + * Gregorian calendar backwards to year one. + * + * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap + * second table is needed for interpretation, using a [24-hour linear + * smear](https://developers.google.com/time/smear). + * + * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By + * restricting to that range, we ensure that we can convert to and from [RFC + * 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. + * + * # Examples + * + * Example 1: Compute Timestamp from POSIX `time()`. + * + * Timestamp timestamp; + * timestamp.set_seconds(time(NULL)); + * timestamp.set_nanos(0); + * + * Example 2: Compute Timestamp from POSIX `gettimeofday()`. + * + * struct timeval tv; + * gettimeofday(&tv, NULL); + * + * Timestamp timestamp; + * timestamp.set_seconds(tv.tv_sec); + * timestamp.set_nanos(tv.tv_usec * 1000); + * + * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. + * + * FILETIME ft; + * GetSystemTimeAsFileTime(&ft); + * UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + * + * // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z + * // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. + * Timestamp timestamp; + * timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); + * timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); + * + * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. + * + * long millis = System.currentTimeMillis(); + * + * Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) + * .setNanos((int) ((millis % 1000) * 1000000)).build(); + * + * + * Example 5: Compute Timestamp from current time in Python. + * + * timestamp = Timestamp() + * timestamp.GetCurrentTime() + * + * # JSON Mapping + * + * In JSON format, the Timestamp type is encoded as a string in the + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the + * format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" + * where {year} is always expressed using four digits while {month}, {day}, + * {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional + * seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), + * are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone + * is required. A proto3 JSON serializer should always use UTC (as indicated by + * "Z") when printing the Timestamp type and a proto3 JSON parser should be + * able to accept both UTC and other timezones (as indicated by an offset). + * + * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past + * 01:30 UTC on January 15, 2017. + * + * In JavaScript, one can convert a Date object to this format using the + * standard + * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) + * method. In Python, a standard `datetime.datetime` object can be converted + * to this format using + * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with + * the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use + * the Joda Time's [`ISODateTimeFormat.dateTime()`]( + * http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D + * ) to obtain a formatter capable of generating timestamps in this format. + * + * + */ +export interface Timestamp { + /** + * Represents seconds of UTC time since Unix epoch + * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + * 9999-12-31T23:59:59Z inclusive. + */ + seconds: number; + /** + * Non-negative fractions of a second at nanosecond resolution. Negative + * second values with fractions must still have non-negative nanos values + * that count forward in time. Must be from 0 to 999,999,999 + * inclusive. + */ + nanos: number; +} + +const baseTimestamp: object = { + seconds: 0, + nanos: 0, +}; + +function longToNumber(long: Long) { + if (long.gt(Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +export const Timestamp = { + encode(message: Timestamp, writer: Writer = Writer.create()): Writer { + writer.uint32(8).int64(message.seconds); + writer.uint32(16).int32(message.nanos); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): Timestamp { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseTimestamp) as Timestamp; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.seconds = longToNumber(reader.int64() as Long); + break; + case 2: + message.nanos = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): Timestamp { + const message = Object.create(baseTimestamp) as Timestamp; + if (object.seconds !== undefined && object.seconds !== null) { + message.seconds = Number(object.seconds); + } else { + message.seconds = 0; + } + if (object.nanos !== undefined && object.nanos !== null) { + message.nanos = Number(object.nanos); + } else { + message.nanos = 0; + } + return message; + }, + fromPartial(object: DeepPartial): Timestamp { + const message = Object.create(baseTimestamp) as Timestamp; + if (object.seconds !== undefined && object.seconds !== null) { + message.seconds = object.seconds; + } else { + message.seconds = 0; + } + if (object.nanos !== undefined && object.nanos !== null) { + message.nanos = object.nanos; + } else { + message.nanos = 0; + } + return message; + }, + toJSON(message: Timestamp): unknown { + const obj: any = {}; + obj.seconds = message.seconds || 0; + obj.nanos = message.nanos || 0; + return obj; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | undefined; +type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; \ No newline at end of file diff --git a/integration/simple-nullable-optionals/google/protobuf/wrappers.bin b/integration/simple-nullable-optionals/google/protobuf/wrappers.bin new file mode 100644 index 0000000000000000000000000000000000000000..7067f2a7335861a611992cad3d71f49a3373ed2e GIT binary patch literal 4316 zcmbtXOK;oQ6{a4x6~>u~nJxkYP0lzSOp+?{z)1&1CS6bxZ8Mc6m89%CO#xHlmCOM} z8hqGr(O=VL7wvB-`j@)svfsI+Ua1`i7%lAkIFIjq=iGBI)hClEngn9^B978%n2mR@ zV)x=g#7RT0Y}aa)hxZ>>ca*>U^BeUJVeRYOSgs0VJI~bn?I;@t;@Ax`q5VQ#yX3*+ z5|y5ptbDSwqpJHssl$T`8F#X}p4KkIP44e&a6ROUH;0!Xa_iiw>Hj^m~-6-^6 zCiIPZuNg(b4R&fX5!kWQ*bgT+2;G|r0Z{Wg70C@Un}tmCuj->wG;MssZu7xOk*hu@ zuV4D`a^k1w*{}iO-ANR<;bf85i}dbrzIH+9Jm zQxT8OfkV(Zil=U>QY*T+j{V7bN_*db|Jy==%y85o(u08HUP7@*M0_c{hDzsYdXc={ z-Sxz!2qIaV1rr>nEK`7Z1>_83S5?Upo}YmEVV3$)NN(s+mI%P1B+BBEkSjw!bmMD@ zE2+~Jn4>6`|Dy~zr;+E6{gKNS>g2|P;Cbq&sqhFsFMSV#^xRGHhocLE=nAQ(Q51SU zD ze}tWwP$?qz-5^;|Efa)Im6mfm52`DWKxnez(47hUN4KH6BX{)fDH(t4TZU~@&m!}v-!Wkg<}JPJn1)>^v)k$n z+Gh8lPOw1To}*I7JTe`~Ila1c`~P~>+ovPLY8}Fi-ZVR=b1EI~n@*RV?e{E|bn5Gt zW3~nz-J<@$>i2AeII^~Bw>r9cWV9Rb51(Wl8(oL&L%q{k4OFGxN!PF#{c@PpG=NfX zb_{j}*QYRQ87+qcT8vsC4*)xLmF&LJGBE-l258W&(|TdlHvTYx6gH`?AL$2%P0#OQ z1&L`5EaQk#g9>}lv>nqKI0hZ`dTmLzZCJ-<%dmf>PLJu|r-5w%NLzPw=^8db7xocA zb6}g2IJ4^*mNn=*X0Q7KSv~{A)U18jZ`J|x3;$+eDFbZEy zMUuD^@q*;l)gb2_mqI>~4H+kOoCT^>71N>cJZ`{qBgw4MoE6svj<0;BY}o+g&u+|2M{`K81dcHG5qFq~5bY6d#$C8XFTjAAb!A5`s#=AI`Qj@DPZ@asl~&>5L@iep zt-7cDrnLR1S$c4b$y7+H&pi30JCpqjRJ^xQp|QB&D|^<>nbWhSr{5W&MM}XiL#A*U z;a90Dz`R#`q>@^#DrH5hyeU80e#D8!?ZEHGIcJ;d8cVFPNKGwf(0+68tJ$oA*_NgX z1W?kdZuW`V{a>4F6qO+tws}>0$*v1c3fs zc|+pG49f3n4`wrXJp_QhU7*Jd%IyL@W%A595CD3opdT|RI|coi0libuzou0`D1ZJe{a*r~>Hj?bH9+`K(7(p`Kd#XKNdS;Q z03QqbF@yHUf_}^pz(>#@p?>hKF;qlpdvx>J9paJyyebq>TUZ*6bMQ_Rj*@_>DW;g#3rvBeC!-p1x^hA_OZAIf8}p;doU zrnlxHKZ$n-vh)GD2xa9MZvetiMIJU7elX8NF1Hc@BoIJQ3~E|6 zR%@5aCQw!H;@4FKZe7}OYkIzI-|#R_@|0DW2<1I(bD7RLZHpidue4cFyG G|N4K5yMa#t literal 0 HcmV?d00001 diff --git a/integration/simple-nullable-optionals/google/protobuf/wrappers.proto b/integration/simple-nullable-optionals/google/protobuf/wrappers.proto new file mode 100644 index 000000000..01947639a --- /dev/null +++ b/integration/simple-nullable-optionals/google/protobuf/wrappers.proto @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Wrappers for primitive (non-message) types. These types are useful +// for embedding primitives in the `google.protobuf.Any` type and for places +// where we need to distinguish between the absence of a primitive +// typed field and its default value. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/wrappers"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "WrappersProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// Wrapper message for `double`. +// +// The JSON representation for `DoubleValue` is JSON number. +message DoubleValue { + // The double value. + double value = 1; +} + +// Wrapper message for `float`. +// +// The JSON representation for `FloatValue` is JSON number. +message FloatValue { + // The float value. + float value = 1; +} + +// Wrapper message for `int64`. +// +// The JSON representation for `Int64Value` is JSON string. +message Int64Value { + // The int64 value. + int64 value = 1; +} + +// Wrapper message for `uint64`. +// +// The JSON representation for `UInt64Value` is JSON string. +message UInt64Value { + // The uint64 value. + uint64 value = 1; +} + +// Wrapper message for `int32`. +// +// The JSON representation for `Int32Value` is JSON number. +message Int32Value { + // The int32 value. + int32 value = 1; +} + +// Wrapper message for `uint32`. +// +// The JSON representation for `UInt32Value` is JSON number. +message UInt32Value { + // The uint32 value. + uint32 value = 1; +} + +// Wrapper message for `bool`. +// +// The JSON representation for `BoolValue` is JSON `true` and `false`. +message BoolValue { + // The bool value. + bool value = 1; +} + +// Wrapper message for `string`. +// +// The JSON representation for `StringValue` is JSON string. +message StringValue { + // The string value. + string value = 1; +} + +// Wrapper message for `bytes`. +// +// The JSON representation for `BytesValue` is JSON string. +message BytesValue { + // The bytes value. + bytes value = 1; +} diff --git a/integration/simple-nullable-optionals/google/protobuf/wrappers.ts b/integration/simple-nullable-optionals/google/protobuf/wrappers.ts new file mode 100644 index 000000000..d02d409ad --- /dev/null +++ b/integration/simple-nullable-optionals/google/protobuf/wrappers.ts @@ -0,0 +1,609 @@ +import * as Long from 'long'; +import { Writer, Reader } from 'protobufjs/minimal'; + + +/** + * Wrapper message for `double`. + * + * The JSON representation for `DoubleValue` is JSON number. + */ +export interface DoubleValue { + /** + * The double value. + */ + value: number; +} + +/** + * Wrapper message for `float`. + * + * The JSON representation for `FloatValue` is JSON number. + */ +export interface FloatValue { + /** + * The float value. + */ + value: number; +} + +/** + * Wrapper message for `int64`. + * + * The JSON representation for `Int64Value` is JSON string. + */ +export interface Int64Value { + /** + * The int64 value. + */ + value: number; +} + +/** + * Wrapper message for `uint64`. + * + * The JSON representation for `UInt64Value` is JSON string. + */ +export interface UInt64Value { + /** + * The uint64 value. + */ + value: number; +} + +/** + * Wrapper message for `int32`. + * + * The JSON representation for `Int32Value` is JSON number. + */ +export interface Int32Value { + /** + * The int32 value. + */ + value: number; +} + +/** + * Wrapper message for `uint32`. + * + * The JSON representation for `UInt32Value` is JSON number. + */ +export interface UInt32Value { + /** + * The uint32 value. + */ + value: number; +} + +/** + * Wrapper message for `bool`. + * + * The JSON representation for `BoolValue` is JSON `true` and `false`. + */ +export interface BoolValue { + /** + * The bool value. + */ + value: boolean; +} + +/** + * Wrapper message for `string`. + * + * The JSON representation for `StringValue` is JSON string. + */ +export interface StringValue { + /** + * The string value. + */ + value: string; +} + +/** + * Wrapper message for `bytes`. + * + * The JSON representation for `BytesValue` is JSON string. + */ +export interface BytesValue { + /** + * The bytes value. + */ + value: Uint8Array; +} + +const baseDoubleValue: object = { + value: 0, +}; + +const baseFloatValue: object = { + value: 0, +}; + +const baseInt64Value: object = { + value: 0, +}; + +const baseUInt64Value: object = { + value: 0, +}; + +const baseInt32Value: object = { + value: 0, +}; + +const baseUInt32Value: object = { + value: 0, +}; + +const baseBoolValue: object = { + value: false, +}; + +const baseStringValue: object = { + value: "", +}; + +const baseBytesValue: object = { + value: undefined, +}; + +function longToNumber(long: Long) { + if (long.gt(Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +export const DoubleValue = { + encode(message: DoubleValue, writer: Writer = Writer.create()): Writer { + writer.uint32(9).double(message.value); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): DoubleValue { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseDoubleValue) as DoubleValue; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.double(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): DoubleValue { + const message = Object.create(baseDoubleValue) as DoubleValue; + if (object.value !== undefined && object.value !== null) { + message.value = Number(object.value); + } else { + message.value = 0; + } + return message; + }, + fromPartial(object: DeepPartial): DoubleValue { + const message = Object.create(baseDoubleValue) as DoubleValue; + if (object.value !== undefined && object.value !== null) { + message.value = object.value; + } else { + message.value = 0; + } + return message; + }, + toJSON(message: DoubleValue): unknown { + const obj: any = {}; + obj.value = message.value || 0; + return obj; + }, +}; + +export const FloatValue = { + encode(message: FloatValue, writer: Writer = Writer.create()): Writer { + writer.uint32(13).float(message.value); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): FloatValue { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseFloatValue) as FloatValue; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.float(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): FloatValue { + const message = Object.create(baseFloatValue) as FloatValue; + if (object.value !== undefined && object.value !== null) { + message.value = Number(object.value); + } else { + message.value = 0; + } + return message; + }, + fromPartial(object: DeepPartial): FloatValue { + const message = Object.create(baseFloatValue) as FloatValue; + if (object.value !== undefined && object.value !== null) { + message.value = object.value; + } else { + message.value = 0; + } + return message; + }, + toJSON(message: FloatValue): unknown { + const obj: any = {}; + obj.value = message.value || 0; + return obj; + }, +}; + +export const Int64Value = { + encode(message: Int64Value, writer: Writer = Writer.create()): Writer { + writer.uint32(8).int64(message.value); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): Int64Value { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseInt64Value) as Int64Value; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = longToNumber(reader.int64() as Long); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): Int64Value { + const message = Object.create(baseInt64Value) as Int64Value; + if (object.value !== undefined && object.value !== null) { + message.value = Number(object.value); + } else { + message.value = 0; + } + return message; + }, + fromPartial(object: DeepPartial): Int64Value { + const message = Object.create(baseInt64Value) as Int64Value; + if (object.value !== undefined && object.value !== null) { + message.value = object.value; + } else { + message.value = 0; + } + return message; + }, + toJSON(message: Int64Value): unknown { + const obj: any = {}; + obj.value = message.value || 0; + return obj; + }, +}; + +export const UInt64Value = { + encode(message: UInt64Value, writer: Writer = Writer.create()): Writer { + writer.uint32(8).uint64(message.value); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): UInt64Value { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseUInt64Value) as UInt64Value; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = longToNumber(reader.uint64() as Long); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): UInt64Value { + const message = Object.create(baseUInt64Value) as UInt64Value; + if (object.value !== undefined && object.value !== null) { + message.value = Number(object.value); + } else { + message.value = 0; + } + return message; + }, + fromPartial(object: DeepPartial): UInt64Value { + const message = Object.create(baseUInt64Value) as UInt64Value; + if (object.value !== undefined && object.value !== null) { + message.value = object.value; + } else { + message.value = 0; + } + return message; + }, + toJSON(message: UInt64Value): unknown { + const obj: any = {}; + obj.value = message.value || 0; + return obj; + }, +}; + +export const Int32Value = { + encode(message: Int32Value, writer: Writer = Writer.create()): Writer { + writer.uint32(8).int32(message.value); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): Int32Value { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseInt32Value) as Int32Value; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): Int32Value { + const message = Object.create(baseInt32Value) as Int32Value; + if (object.value !== undefined && object.value !== null) { + message.value = Number(object.value); + } else { + message.value = 0; + } + return message; + }, + fromPartial(object: DeepPartial): Int32Value { + const message = Object.create(baseInt32Value) as Int32Value; + if (object.value !== undefined && object.value !== null) { + message.value = object.value; + } else { + message.value = 0; + } + return message; + }, + toJSON(message: Int32Value): unknown { + const obj: any = {}; + obj.value = message.value || 0; + return obj; + }, +}; + +export const UInt32Value = { + encode(message: UInt32Value, writer: Writer = Writer.create()): Writer { + writer.uint32(8).uint32(message.value); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): UInt32Value { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseUInt32Value) as UInt32Value; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): UInt32Value { + const message = Object.create(baseUInt32Value) as UInt32Value; + if (object.value !== undefined && object.value !== null) { + message.value = Number(object.value); + } else { + message.value = 0; + } + return message; + }, + fromPartial(object: DeepPartial): UInt32Value { + const message = Object.create(baseUInt32Value) as UInt32Value; + if (object.value !== undefined && object.value !== null) { + message.value = object.value; + } else { + message.value = 0; + } + return message; + }, + toJSON(message: UInt32Value): unknown { + const obj: any = {}; + obj.value = message.value || 0; + return obj; + }, +}; + +export const BoolValue = { + encode(message: BoolValue, writer: Writer = Writer.create()): Writer { + writer.uint32(8).bool(message.value); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): BoolValue { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseBoolValue) as BoolValue; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): BoolValue { + const message = Object.create(baseBoolValue) as BoolValue; + if (object.value !== undefined && object.value !== null) { + message.value = Boolean(object.value); + } else { + message.value = false; + } + return message; + }, + fromPartial(object: DeepPartial): BoolValue { + const message = Object.create(baseBoolValue) as BoolValue; + if (object.value !== undefined && object.value !== null) { + message.value = object.value; + } else { + message.value = false; + } + return message; + }, + toJSON(message: BoolValue): unknown { + const obj: any = {}; + obj.value = message.value || false; + return obj; + }, +}; + +export const StringValue = { + encode(message: StringValue, writer: Writer = Writer.create()): Writer { + writer.uint32(10).string(message.value); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): StringValue { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseStringValue) as StringValue; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): StringValue { + const message = Object.create(baseStringValue) as StringValue; + if (object.value !== undefined && object.value !== null) { + message.value = String(object.value); + } else { + message.value = ""; + } + return message; + }, + fromPartial(object: DeepPartial): StringValue { + const message = Object.create(baseStringValue) as StringValue; + if (object.value !== undefined && object.value !== null) { + message.value = object.value; + } else { + message.value = ""; + } + return message; + }, + toJSON(message: StringValue): unknown { + const obj: any = {}; + obj.value = message.value || ""; + return obj; + }, +}; + +export const BytesValue = { + encode(message: BytesValue, writer: Writer = Writer.create()): Writer { + writer.uint32(10).bytes(message.value); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): BytesValue { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseBytesValue) as BytesValue; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): BytesValue { + const message = Object.create(baseBytesValue) as BytesValue; + if (object.value !== undefined && object.value !== null) { + message.value = bytesFromBase64(object.value); + } + return message; + }, + fromPartial(object: DeepPartial): BytesValue { + const message = Object.create(baseBytesValue) as BytesValue; + if (object.value !== undefined && object.value !== null) { + message.value = object.value; + } + return message; + }, + toJSON(message: BytesValue): unknown { + const obj: any = {}; + obj.value = (message.value !== undefined && message.value !== null) ? base64FromBytes(message.value) : undefined; + return obj; + }, +}; + +interface WindowBase64 { + atob(b64: string): string; + btoa(bin: string): string; +} + +const windowBase64 = (globalThis as unknown as WindowBase64); +const atob = windowBase64.atob || ((b64: string) => Buffer.from(b64, 'base64').toString('binary')); +const btoa = windowBase64.btoa || ((bin: string) => Buffer.from(bin, 'binary').toString('base64')); + +function bytesFromBase64(b64: string): Uint8Array { + const bin = atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; +} + +function base64FromBytes(arr: Uint8Array): string { + const bin: string[] = []; + for (let i = 0; i < arr.byteLength; ++i) { + bin.push(String.fromCharCode(arr[i])); + } + return btoa(bin.join('')); +} +type Builtin = Date | Function | Uint8Array | string | number | undefined; +type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; \ No newline at end of file diff --git a/integration/simple-nullable-optionals/import_dir/thing.bin b/integration/simple-nullable-optionals/import_dir/thing.bin new file mode 100644 index 0000000000000000000000000000000000000000..ae41fa9a60d2488be1cd4603803c6f638585b91f GIT binary patch literal 6408 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 3) { + case 1: + message.createdAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): ImportedThing { + const message = Object.create(baseImportedThing) as ImportedThing; + if (object.createdAt !== undefined && object.createdAt !== null) { + message.createdAt = fromJsonTimestamp(object.createdAt); + } else { + message.createdAt = null; + } + return message; + }, + fromPartial(object: DeepPartial): ImportedThing { + const message = Object.create(baseImportedThing) as ImportedThing; + if (object.createdAt !== undefined && object.createdAt !== null) { + message.createdAt = object.createdAt; + } else { + message.createdAt = null; + } + return message; + }, + toJSON(message: ImportedThing): unknown { + const obj: any = {}; + obj.createdAt = (message.createdAt !== undefined && message.createdAt !== null) ? message.createdAt.toISOString() : null; + return obj; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | undefined; +type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; \ No newline at end of file diff --git a/integration/simple-nullable-optionals/nullable-optionals-test.ts b/integration/simple-nullable-optionals/nullable-optionals-test.ts new file mode 100644 index 000000000..2e3f75e12 --- /dev/null +++ b/integration/simple-nullable-optionals/nullable-optionals-test.ts @@ -0,0 +1,20 @@ +import { Simple, SimpleWithWrappers, StateEnum } from './simple' + +describe('simple', () => { + it('generates types correctly', () => { + const simple: Simple = { + name: 'Bob', + age: 33, + state: StateEnum.ON, + grandChildren: [], + coins: [], + snacks: [], + oldStates: [], + } + + const simpleWithWrappers: SimpleWithWrappers = { + coins: [], + snacks: [], + } + }) +}) diff --git a/integration/simple-nullable-optionals/parameters.txt b/integration/simple-nullable-optionals/parameters.txt new file mode 100644 index 000000000..c8ea841ca --- /dev/null +++ b/integration/simple-nullable-optionals/parameters.txt @@ -0,0 +1 @@ +nullableOptionals=true diff --git a/integration/simple-nullable-optionals/simple.bin b/integration/simple-nullable-optionals/simple.bin new file mode 100644 index 0000000000000000000000000000000000000000..db0c8b07dc851fbeafac838d67ced93929176596 GIT binary patch literal 17013 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 entitiesById = 1; + map nameLookup = 2; + map intLookup = 3; +} + +message SimpleWithSnakeCaseMap { + map entities_by_id = 1; +} + +service PingService { + rpc ping(PingRequest) returns (PingResponse); +} + +message PingRequest { + string input = 1; +} + +message PingResponse { + string output = 1; +} + +message Numbers { + double double = 1; + float float = 2; + int32 int32 = 3; + int64 int64 = 4; + uint32 uint32 = 5; + uint64 uint64 = 6; + sint32 sint32 = 7; + sint64 sint64 = 8; + fixed32 fixed32 = 9; + fixed64 fixed64 = 10; + sfixed32 sfixed32 = 11; + sfixed64 sfixed64 = 12; +} diff --git a/integration/simple-nullable-optionals/simple.ts b/integration/simple-nullable-optionals/simple.ts new file mode 100644 index 000000000..a855779e3 --- /dev/null +++ b/integration/simple-nullable-optionals/simple.ts @@ -0,0 +1,1854 @@ +// Adding a comment to the syntax will become the first +// comment in the output source file. +// +import { ImportedThing } from './import_dir/thing'; +import { Reader, Writer } from 'protobufjs/minimal'; +import { Timestamp } from './google/protobuf/timestamp'; +import * as Long from 'long'; +import { StringValue, Int32Value, BoolValue } from './google/protobuf/wrappers'; + + +/** + * * Example comment on the Simple message */ +export interface Simple { + /** + * Name field + */ + name: string; + /** + * Age */ + age: number; + /** + * This comment will also attach + */ + createdAt?: Date | null; + child?: Child | null; + state: StateEnum; + grandChildren: Child[]; + coins: number[]; + snacks: string[]; + oldStates: StateEnum[]; + /** + * A thing (imported from thing) + */ + thing?: ImportedThing | null; +} + +export interface Child { + name: string; + type: Child_Type; +} + +export interface Nested { + name: string; + message?: Nested_InnerMessage | null; + state: Nested_InnerEnum; +} + +/** + * Comment for a nested message * / + */ +export interface Nested_InnerMessage { + name: string; + deep?: Nested_InnerMessage_DeepMessage | null; +} + +export interface Nested_InnerMessage_DeepMessage { + name: string; +} + +export interface OneOfMessage { + first?: string | null; + last?: string | null; +} + +export interface SimpleWithWrappers { + name?: string | null; + age?: number | null; + enabled?: boolean | null; + coins: number[]; + snacks: string[]; +} + +export interface Entity { + id: number; +} + +export interface SimpleWithMap { + entitiesById: { [key: number]: Entity }; + nameLookup: { [key: string]: string }; + intLookup: { [key: number]: number }; +} + +export interface SimpleWithMap_EntitiesByIdEntry { + key: number; + value?: Entity | null; +} + +export interface SimpleWithMap_NameLookupEntry { + key: string; + value: string; +} + +export interface SimpleWithMap_IntLookupEntry { + key: number; + value: number; +} + +export interface SimpleWithSnakeCaseMap { + entitiesById: { [key: number]: Entity }; +} + +export interface SimpleWithSnakeCaseMap_EntitiesByIdEntry { + key: number; + value?: Entity | null; +} + +export interface PingRequest { + input: string; +} + +export interface PingResponse { + output: string; +} + +export interface Numbers { + double: number; + float: number; + int32: number; + int64: number; + uint32: number; + uint64: number; + sint32: number; + sint64: number; + fixed32: number; + fixed64: number; + sfixed32: number; + sfixed64: number; +} + +const baseSimple: object = { + name: "", + age: 0, + state: 0, + coins: 0, + snacks: "", + oldStates: 0, +}; + +const baseChild: object = { + name: "", + type: 0, +}; + +const baseNested: object = { + name: "", + state: 0, +}; + +const baseNested_InnerMessage: object = { + name: "", +}; + +const baseNested_InnerMessage_DeepMessage: object = { + name: "", +}; + +const baseOneOfMessage: object = { +}; + +const baseSimpleWithWrappers: object = { +}; + +const baseEntity: object = { + id: 0, +}; + +const baseSimpleWithMap: object = { +}; + +const baseSimpleWithMap_EntitiesByIdEntry: object = { + key: 0, +}; + +const baseSimpleWithMap_NameLookupEntry: object = { + key: "", + value: "", +}; + +const baseSimpleWithMap_IntLookupEntry: object = { + key: 0, + value: 0, +}; + +const baseSimpleWithSnakeCaseMap: object = { +}; + +const baseSimpleWithSnakeCaseMap_EntitiesByIdEntry: object = { + key: 0, +}; + +const basePingRequest: object = { + input: "", +}; + +const basePingResponse: object = { + output: "", +}; + +const baseNumbers: object = { + double: 0, + float: 0, + int32: 0, + int64: 0, + uint32: 0, + uint64: 0, + sint32: 0, + sint64: 0, + fixed32: 0, + fixed64: 0, + sfixed32: 0, + sfixed64: 0, +}; + +export interface PingService { + + ping(request: PingRequest): Promise; + +} + +export class PingServiceClientImpl implements PingService { + + private readonly rpc: Rpc; + + constructor(rpc: Rpc) { + this.rpc = rpc; + } + + ping(request: PingRequest): Promise { + const data = PingRequest.encode(request).finish(); + const promise = this.rpc.request("simple.PingService", "ping", data); + return promise.then(data => PingResponse.decode(new Reader(data))); + } + +} + +interface Rpc { + + request(service: string, method: string, data: Uint8Array): Promise; + +} + +function fromJsonTimestamp(o: any): Date { + if (o instanceof Date) { + return o; + } else if (typeof o === "string") { + return new Date(o); + } else { + return fromTimestamp(Timestamp.fromJSON(o)); + } +} + +function toTimestamp(date: Date): Timestamp { + const seconds = date.getTime() / 1_000; + const nanos = (date.getTime() % 1_000) * 1_000_000; + return { seconds, nanos }; +} + +function fromTimestamp(t: Timestamp): Date { + let millis = t.seconds * 1_000; + millis += t.nanos / 1_000_000; + return new Date(millis); +} + +function longToNumber(long: Long) { + if (long.gt(Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +export const StateEnum = { + UNKNOWN: 0 as const, + ON: 2 as const, + OFF: 3 as const, + UNRECOGNIZED: -1 as const, + fromJSON(object: any): StateEnum { + switch (object) { + case 0: + case "UNKNOWN": + return StateEnum.UNKNOWN; + case 2: + case "ON": + return StateEnum.ON; + case 3: + case "OFF": + return StateEnum.OFF; + case -1: + case "UNRECOGNIZED": + default: + return StateEnum.UNRECOGNIZED; + } + }, + toJSON(object: StateEnum): string { + switch (object) { + case StateEnum.UNKNOWN: + return "UNKNOWN"; + case StateEnum.ON: + return "ON"; + case StateEnum.OFF: + return "OFF"; + default: + return "UNKNOWN"; + } + }, +} + +export type StateEnum = 0 | 2 | 3 | -1; + +export const Child_Type = { + UNKNOWN: 0 as const, + GOOD: 1 as const, + BAD: 2 as const, + UNRECOGNIZED: -1 as const, + fromJSON(object: any): Child_Type { + switch (object) { + case 0: + case "UNKNOWN": + return Child_Type.UNKNOWN; + case 1: + case "GOOD": + return Child_Type.GOOD; + case 2: + case "BAD": + return Child_Type.BAD; + case -1: + case "UNRECOGNIZED": + default: + return Child_Type.UNRECOGNIZED; + } + }, + toJSON(object: Child_Type): string { + switch (object) { + case Child_Type.UNKNOWN: + return "UNKNOWN"; + case Child_Type.GOOD: + return "GOOD"; + case Child_Type.BAD: + return "BAD"; + default: + return "UNKNOWN"; + } + }, +} + +export type Child_Type = 0 | 1 | 2 | -1; + +export const Nested_InnerEnum = { + UNKNOWN_INNER: 0 as const, + GOOD: 100 as const, + BAD: 1000 as const, + UNRECOGNIZED: -1 as const, + fromJSON(object: any): Nested_InnerEnum { + switch (object) { + case 0: + case "UNKNOWN_INNER": + return Nested_InnerEnum.UNKNOWN_INNER; + case 100: + case "GOOD": + return Nested_InnerEnum.GOOD; + case 1000: + case "BAD": + return Nested_InnerEnum.BAD; + case -1: + case "UNRECOGNIZED": + default: + return Nested_InnerEnum.UNRECOGNIZED; + } + }, + toJSON(object: Nested_InnerEnum): string { + switch (object) { + case Nested_InnerEnum.UNKNOWN_INNER: + return "UNKNOWN_INNER"; + case Nested_InnerEnum.GOOD: + return "GOOD"; + case Nested_InnerEnum.BAD: + return "BAD"; + default: + return "UNKNOWN"; + } + }, +} + +export type Nested_InnerEnum = 0 | 100 | 1000 | -1; + +export const Simple = { + encode(message: Simple, writer: Writer = Writer.create()): Writer { + writer.uint32(10).string(message.name); + writer.uint32(16).int32(message.age); + if (message.createdAt !== undefined && message.createdAt !== null) { + Timestamp.encode(toTimestamp(message.createdAt), writer.uint32(74).fork()).ldelim(); + } + if (message.child !== undefined && message.child !== null) { + Child.encode(message.child, writer.uint32(26).fork()).ldelim(); + } + writer.uint32(32).int32(message.state); + for (const v of message.grandChildren) { + Child.encode(v!, writer.uint32(42).fork()).ldelim(); + } + writer.uint32(50).fork(); + for (const v of message.coins) { + writer.int32(v); + } + writer.ldelim(); + for (const v of message.snacks) { + writer.uint32(58).string(v!); + } + writer.uint32(66).fork(); + for (const v of message.oldStates) { + writer.int32(v); + } + writer.ldelim(); + if (message.thing !== undefined && message.thing !== null) { + ImportedThing.encode(message.thing, writer.uint32(82).fork()).ldelim(); + } + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): Simple { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseSimple) as Simple; + message.grandChildren = []; + message.coins = []; + message.snacks = []; + message.oldStates = []; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.name = reader.string(); + break; + case 2: + message.age = reader.int32(); + break; + case 9: + message.createdAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + break; + case 3: + message.child = Child.decode(reader, reader.uint32()); + break; + case 4: + message.state = reader.int32() as any; + break; + case 5: + message.grandChildren.push(Child.decode(reader, reader.uint32())); + break; + case 6: + if ((tag & 7) === 2) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.coins.push(reader.int32()); + } + } else { + message.coins.push(reader.int32()); + } + break; + case 7: + message.snacks.push(reader.string()); + break; + case 8: + if ((tag & 7) === 2) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.oldStates.push(reader.int32() as any); + } + } else { + message.oldStates.push(reader.int32() as any); + } + break; + case 10: + message.thing = ImportedThing.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): Simple { + const message = Object.create(baseSimple) as Simple; + message.grandChildren = []; + message.coins = []; + message.snacks = []; + message.oldStates = []; + if (object.name !== undefined && object.name !== null) { + message.name = String(object.name); + } else { + message.name = ""; + } + if (object.age !== undefined && object.age !== null) { + message.age = Number(object.age); + } else { + message.age = 0; + } + if (object.createdAt !== undefined && object.createdAt !== null) { + message.createdAt = fromJsonTimestamp(object.createdAt); + } else { + message.createdAt = null; + } + if (object.child !== undefined && object.child !== null) { + message.child = Child.fromJSON(object.child); + } else { + message.child = null; + } + if (object.state !== undefined && object.state !== null) { + message.state = StateEnum.fromJSON(object.state); + } else { + message.state = 0; + } + if (object.grandChildren !== undefined && object.grandChildren !== null) { + for (const e of object.grandChildren) { + message.grandChildren.push(Child.fromJSON(e)); + } + } + if (object.coins !== undefined && object.coins !== null) { + for (const e of object.coins) { + message.coins.push(Number(e)); + } + } + if (object.snacks !== undefined && object.snacks !== null) { + for (const e of object.snacks) { + message.snacks.push(String(e)); + } + } + if (object.oldStates !== undefined && object.oldStates !== null) { + for (const e of object.oldStates) { + message.oldStates.push(StateEnum.fromJSON(e)); + } + } + if (object.thing !== undefined && object.thing !== null) { + message.thing = ImportedThing.fromJSON(object.thing); + } else { + message.thing = null; + } + return message; + }, + fromPartial(object: DeepPartial): Simple { + const message = Object.create(baseSimple) as Simple; + message.grandChildren = []; + message.coins = []; + message.snacks = []; + message.oldStates = []; + if (object.name !== undefined && object.name !== null) { + message.name = object.name; + } else { + message.name = ""; + } + if (object.age !== undefined && object.age !== null) { + message.age = object.age; + } else { + message.age = 0; + } + if (object.createdAt !== undefined && object.createdAt !== null) { + message.createdAt = object.createdAt; + } else { + message.createdAt = null; + } + if (object.child !== undefined && object.child !== null) { + message.child = Child.fromPartial(object.child); + } else { + message.child = null; + } + if (object.state !== undefined && object.state !== null) { + message.state = object.state; + } else { + message.state = 0; + } + if (object.grandChildren !== undefined && object.grandChildren !== null) { + for (const e of object.grandChildren) { + message.grandChildren.push(Child.fromPartial(e)); + } + } + if (object.coins !== undefined && object.coins !== null) { + for (const e of object.coins) { + message.coins.push(e); + } + } + if (object.snacks !== undefined && object.snacks !== null) { + for (const e of object.snacks) { + message.snacks.push(e); + } + } + if (object.oldStates !== undefined && object.oldStates !== null) { + for (const e of object.oldStates) { + message.oldStates.push(e); + } + } + if (object.thing !== undefined && object.thing !== null) { + message.thing = ImportedThing.fromPartial(object.thing); + } else { + message.thing = null; + } + return message; + }, + toJSON(message: Simple): unknown { + const obj: any = {}; + obj.name = message.name || ""; + obj.age = message.age || 0; + obj.createdAt = (message.createdAt !== undefined && message.createdAt !== null) ? message.createdAt.toISOString() : null; + obj.child = message.child ? Child.toJSON(message.child) : null; + obj.state = StateEnum.toJSON(message.state); + if (message.grandChildren) { + obj.grandChildren = message.grandChildren.map(e => e ? Child.toJSON(e) : null); + } else { + obj.grandChildren = []; + } + if (message.coins) { + obj.coins = message.coins.map(e => e || 0); + } else { + obj.coins = []; + } + if (message.snacks) { + obj.snacks = message.snacks.map(e => e || ""); + } else { + obj.snacks = []; + } + if (message.oldStates) { + obj.oldStates = message.oldStates.map(e => StateEnum.toJSON(e)); + } else { + obj.oldStates = []; + } + obj.thing = message.thing ? ImportedThing.toJSON(message.thing) : null; + return obj; + }, +}; + +export const Child = { + encode(message: Child, writer: Writer = Writer.create()): Writer { + writer.uint32(10).string(message.name); + writer.uint32(16).int32(message.type); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): Child { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseChild) as Child; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.name = reader.string(); + break; + case 2: + message.type = reader.int32() as any; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): Child { + const message = Object.create(baseChild) as Child; + if (object.name !== undefined && object.name !== null) { + message.name = String(object.name); + } else { + message.name = ""; + } + if (object.type !== undefined && object.type !== null) { + message.type = Child_Type.fromJSON(object.type); + } else { + message.type = 0; + } + return message; + }, + fromPartial(object: DeepPartial): Child { + const message = Object.create(baseChild) as Child; + if (object.name !== undefined && object.name !== null) { + message.name = object.name; + } else { + message.name = ""; + } + if (object.type !== undefined && object.type !== null) { + message.type = object.type; + } else { + message.type = 0; + } + return message; + }, + toJSON(message: Child): unknown { + const obj: any = {}; + obj.name = message.name || ""; + obj.type = Child_Type.toJSON(message.type); + return obj; + }, +}; + +export const Nested = { + encode(message: Nested, writer: Writer = Writer.create()): Writer { + writer.uint32(10).string(message.name); + if (message.message !== undefined && message.message !== null) { + Nested_InnerMessage.encode(message.message, writer.uint32(18).fork()).ldelim(); + } + writer.uint32(24).int32(message.state); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): Nested { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseNested) as Nested; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.name = reader.string(); + break; + case 2: + message.message = Nested_InnerMessage.decode(reader, reader.uint32()); + break; + case 3: + message.state = reader.int32() as any; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): Nested { + const message = Object.create(baseNested) as Nested; + if (object.name !== undefined && object.name !== null) { + message.name = String(object.name); + } else { + message.name = ""; + } + if (object.message !== undefined && object.message !== null) { + message.message = Nested_InnerMessage.fromJSON(object.message); + } else { + message.message = null; + } + if (object.state !== undefined && object.state !== null) { + message.state = Nested_InnerEnum.fromJSON(object.state); + } else { + message.state = 0; + } + return message; + }, + fromPartial(object: DeepPartial): Nested { + const message = Object.create(baseNested) as Nested; + if (object.name !== undefined && object.name !== null) { + message.name = object.name; + } else { + message.name = ""; + } + if (object.message !== undefined && object.message !== null) { + message.message = Nested_InnerMessage.fromPartial(object.message); + } else { + message.message = null; + } + if (object.state !== undefined && object.state !== null) { + message.state = object.state; + } else { + message.state = 0; + } + return message; + }, + toJSON(message: Nested): unknown { + const obj: any = {}; + obj.name = message.name || ""; + obj.message = message.message ? Nested_InnerMessage.toJSON(message.message) : null; + obj.state = Nested_InnerEnum.toJSON(message.state); + return obj; + }, +}; + +export const Nested_InnerMessage = { + encode(message: Nested_InnerMessage, writer: Writer = Writer.create()): Writer { + writer.uint32(10).string(message.name); + if (message.deep !== undefined && message.deep !== null) { + Nested_InnerMessage_DeepMessage.encode(message.deep, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): Nested_InnerMessage { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseNested_InnerMessage) as Nested_InnerMessage; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.name = reader.string(); + break; + case 2: + message.deep = Nested_InnerMessage_DeepMessage.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): Nested_InnerMessage { + const message = Object.create(baseNested_InnerMessage) as Nested_InnerMessage; + if (object.name !== undefined && object.name !== null) { + message.name = String(object.name); + } else { + message.name = ""; + } + if (object.deep !== undefined && object.deep !== null) { + message.deep = Nested_InnerMessage_DeepMessage.fromJSON(object.deep); + } else { + message.deep = null; + } + return message; + }, + fromPartial(object: DeepPartial): Nested_InnerMessage { + const message = Object.create(baseNested_InnerMessage) as Nested_InnerMessage; + if (object.name !== undefined && object.name !== null) { + message.name = object.name; + } else { + message.name = ""; + } + if (object.deep !== undefined && object.deep !== null) { + message.deep = Nested_InnerMessage_DeepMessage.fromPartial(object.deep); + } else { + message.deep = null; + } + return message; + }, + toJSON(message: Nested_InnerMessage): unknown { + const obj: any = {}; + obj.name = message.name || ""; + obj.deep = message.deep ? Nested_InnerMessage_DeepMessage.toJSON(message.deep) : null; + return obj; + }, +}; + +export const Nested_InnerMessage_DeepMessage = { + encode(message: Nested_InnerMessage_DeepMessage, writer: Writer = Writer.create()): Writer { + writer.uint32(10).string(message.name); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): Nested_InnerMessage_DeepMessage { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseNested_InnerMessage_DeepMessage) as Nested_InnerMessage_DeepMessage; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.name = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): Nested_InnerMessage_DeepMessage { + const message = Object.create(baseNested_InnerMessage_DeepMessage) as Nested_InnerMessage_DeepMessage; + if (object.name !== undefined && object.name !== null) { + message.name = String(object.name); + } else { + message.name = ""; + } + return message; + }, + fromPartial(object: DeepPartial): Nested_InnerMessage_DeepMessage { + const message = Object.create(baseNested_InnerMessage_DeepMessage) as Nested_InnerMessage_DeepMessage; + if (object.name !== undefined && object.name !== null) { + message.name = object.name; + } else { + message.name = ""; + } + return message; + }, + toJSON(message: Nested_InnerMessage_DeepMessage): unknown { + const obj: any = {}; + obj.name = message.name || ""; + return obj; + }, +}; + +export const OneOfMessage = { + encode(message: OneOfMessage, writer: Writer = Writer.create()): Writer { + if (message.first !== undefined && message.first !== null) { + writer.uint32(10).string(message.first); + } + if (message.last !== undefined && message.last !== null) { + writer.uint32(18).string(message.last); + } + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): OneOfMessage { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseOneOfMessage) as OneOfMessage; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.first = reader.string(); + break; + case 2: + message.last = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): OneOfMessage { + const message = Object.create(baseOneOfMessage) as OneOfMessage; + if (object.first !== undefined && object.first !== null) { + message.first = String(object.first); + } else { + message.first = undefined; + } + if (object.last !== undefined && object.last !== null) { + message.last = String(object.last); + } else { + message.last = undefined; + } + return message; + }, + fromPartial(object: DeepPartial): OneOfMessage { + const message = Object.create(baseOneOfMessage) as OneOfMessage; + if (object.first !== undefined && object.first !== null) { + message.first = object.first; + } else { + message.first = undefined; + } + if (object.last !== undefined && object.last !== null) { + message.last = object.last; + } else { + message.last = undefined; + } + return message; + }, + toJSON(message: OneOfMessage): unknown { + const obj: any = {}; + obj.first = message.first || undefined; + obj.last = message.last || undefined; + return obj; + }, +}; + +export const SimpleWithWrappers = { + encode(message: SimpleWithWrappers, writer: Writer = Writer.create()): Writer { + if (message.name !== undefined && message.name !== null) { + StringValue.encode({ value: message.name! }, writer.uint32(10).fork()).ldelim(); + } + if (message.age !== undefined && message.age !== null) { + Int32Value.encode({ value: message.age! }, writer.uint32(18).fork()).ldelim(); + } + if (message.enabled !== undefined && message.enabled !== null) { + BoolValue.encode({ value: message.enabled! }, writer.uint32(26).fork()).ldelim(); + } + for (const v of message.coins) { + Int32Value.encode({ value: v!! }, writer.uint32(50).fork()).ldelim(); + } + for (const v of message.snacks) { + StringValue.encode({ value: v!! }, writer.uint32(58).fork()).ldelim(); + } + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): SimpleWithWrappers { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseSimpleWithWrappers) as SimpleWithWrappers; + message.coins = []; + message.snacks = []; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.name = StringValue.decode(reader, reader.uint32()).value; + break; + case 2: + message.age = Int32Value.decode(reader, reader.uint32()).value; + break; + case 3: + message.enabled = BoolValue.decode(reader, reader.uint32()).value; + break; + case 6: + message.coins.push(Int32Value.decode(reader, reader.uint32()).value); + break; + case 7: + message.snacks.push(StringValue.decode(reader, reader.uint32()).value); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): SimpleWithWrappers { + const message = Object.create(baseSimpleWithWrappers) as SimpleWithWrappers; + message.coins = []; + message.snacks = []; + if (object.name !== undefined && object.name !== null) { + message.name = String(object.name); + } else { + message.name = null; + } + if (object.age !== undefined && object.age !== null) { + message.age = Number(object.age); + } else { + message.age = null; + } + if (object.enabled !== undefined && object.enabled !== null) { + message.enabled = Boolean(object.enabled); + } else { + message.enabled = null; + } + if (object.coins !== undefined && object.coins !== null) { + for (const e of object.coins) { + message.coins.push(Number(e)); + } + } + if (object.snacks !== undefined && object.snacks !== null) { + for (const e of object.snacks) { + message.snacks.push(String(e)); + } + } + return message; + }, + fromPartial(object: DeepPartial): SimpleWithWrappers { + const message = Object.create(baseSimpleWithWrappers) as SimpleWithWrappers; + message.coins = []; + message.snacks = []; + if (object.name !== undefined && object.name !== null) { + message.name = object.name; + } else { + message.name = null; + } + if (object.age !== undefined && object.age !== null) { + message.age = object.age; + } else { + message.age = null; + } + if (object.enabled !== undefined && object.enabled !== null) { + message.enabled = object.enabled; + } else { + message.enabled = null; + } + if (object.coins !== undefined && object.coins !== null) { + for (const e of object.coins) { + message.coins.push(e); + } + } + if (object.snacks !== undefined && object.snacks !== null) { + for (const e of object.snacks) { + message.snacks.push(e); + } + } + return message; + }, + toJSON(message: SimpleWithWrappers): unknown { + const obj: any = {}; + obj.name = message.name || null; + obj.age = message.age || null; + obj.enabled = message.enabled || null; + if (message.coins) { + obj.coins = message.coins.map(e => e || null); + } else { + obj.coins = []; + } + if (message.snacks) { + obj.snacks = message.snacks.map(e => e || null); + } else { + obj.snacks = []; + } + return obj; + }, +}; + +export const Entity = { + encode(message: Entity, writer: Writer = Writer.create()): Writer { + writer.uint32(8).int32(message.id); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): Entity { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseEntity) as Entity; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): Entity { + const message = Object.create(baseEntity) as Entity; + if (object.id !== undefined && object.id !== null) { + message.id = Number(object.id); + } else { + message.id = 0; + } + return message; + }, + fromPartial(object: DeepPartial): Entity { + const message = Object.create(baseEntity) as Entity; + if (object.id !== undefined && object.id !== null) { + message.id = object.id; + } else { + message.id = 0; + } + return message; + }, + toJSON(message: Entity): unknown { + const obj: any = {}; + obj.id = message.id || 0; + return obj; + }, +}; + +export const SimpleWithMap = { + encode(message: SimpleWithMap, writer: Writer = Writer.create()): Writer { + Object.entries(message.entitiesById).forEach(([key, value]) => { + SimpleWithMap_EntitiesByIdEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim(); + }) + Object.entries(message.nameLookup).forEach(([key, value]) => { + SimpleWithMap_NameLookupEntry.encode({ key: key as any, value }, writer.uint32(18).fork()).ldelim(); + }) + Object.entries(message.intLookup).forEach(([key, value]) => { + SimpleWithMap_IntLookupEntry.encode({ key: key as any, value }, writer.uint32(26).fork()).ldelim(); + }) + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): SimpleWithMap { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseSimpleWithMap) as SimpleWithMap; + message.entitiesById = {}; + message.nameLookup = {}; + message.intLookup = {}; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + const entry1 = SimpleWithMap_EntitiesByIdEntry.decode(reader, reader.uint32()); + if (entry1.value) { + message.entitiesById[entry1.key] = entry1.value; + } + break; + case 2: + const entry2 = SimpleWithMap_NameLookupEntry.decode(reader, reader.uint32()); + if (entry2.value) { + message.nameLookup[entry2.key] = entry2.value; + } + break; + case 3: + const entry3 = SimpleWithMap_IntLookupEntry.decode(reader, reader.uint32()); + if (entry3.value) { + message.intLookup[entry3.key] = entry3.value; + } + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): SimpleWithMap { + const message = Object.create(baseSimpleWithMap) as SimpleWithMap; + message.entitiesById = {}; + message.nameLookup = {}; + message.intLookup = {}; + if (object.entitiesById !== undefined && object.entitiesById !== null) { + Object.entries(object.entitiesById).forEach(([key, value]) => { + message.entitiesById[Number(key)] = Entity.fromJSON(value); + }) + } + if (object.nameLookup !== undefined && object.nameLookup !== null) { + Object.entries(object.nameLookup).forEach(([key, value]) => { + message.nameLookup[key] = String(value); + }) + } + if (object.intLookup !== undefined && object.intLookup !== null) { + Object.entries(object.intLookup).forEach(([key, value]) => { + message.intLookup[Number(key)] = Number(value); + }) + } + return message; + }, + fromPartial(object: DeepPartial): SimpleWithMap { + const message = Object.create(baseSimpleWithMap) as SimpleWithMap; + message.entitiesById = {}; + message.nameLookup = {}; + message.intLookup = {}; + if (object.entitiesById !== undefined && object.entitiesById !== null) { + Object.entries(object.entitiesById).forEach(([key, value]) => { + if (value) { + message.entitiesById[Number(key)] = Entity.fromPartial(value); + } + }) + } + if (object.nameLookup !== undefined && object.nameLookup !== null) { + Object.entries(object.nameLookup).forEach(([key, value]) => { + if (value) { + message.nameLookup[key] = String(value); + } + }) + } + if (object.intLookup !== undefined && object.intLookup !== null) { + Object.entries(object.intLookup).forEach(([key, value]) => { + if (value) { + message.intLookup[Number(key)] = Number(value); + } + }) + } + return message; + }, + toJSON(message: SimpleWithMap): unknown { + const obj: any = {}; + obj.entitiesById = message.entitiesById || null; + obj.nameLookup = message.nameLookup || null; + obj.intLookup = message.intLookup || null; + return obj; + }, +}; + +export const SimpleWithMap_EntitiesByIdEntry = { + encode(message: SimpleWithMap_EntitiesByIdEntry, writer: Writer = Writer.create()): Writer { + writer.uint32(8).int32(message.key); + if (message.value !== undefined && message.value !== null) { + Entity.encode(message.value, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): SimpleWithMap_EntitiesByIdEntry { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseSimpleWithMap_EntitiesByIdEntry) as SimpleWithMap_EntitiesByIdEntry; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = reader.int32(); + break; + case 2: + message.value = Entity.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): SimpleWithMap_EntitiesByIdEntry { + const message = Object.create(baseSimpleWithMap_EntitiesByIdEntry) as SimpleWithMap_EntitiesByIdEntry; + if (object.key !== undefined && object.key !== null) { + message.key = Number(object.key); + } else { + message.key = 0; + } + if (object.value !== undefined && object.value !== null) { + message.value = Entity.fromJSON(object.value); + } else { + message.value = null; + } + return message; + }, + fromPartial(object: DeepPartial): SimpleWithMap_EntitiesByIdEntry { + const message = Object.create(baseSimpleWithMap_EntitiesByIdEntry) as SimpleWithMap_EntitiesByIdEntry; + if (object.key !== undefined && object.key !== null) { + message.key = object.key; + } else { + message.key = 0; + } + if (object.value !== undefined && object.value !== null) { + message.value = Entity.fromPartial(object.value); + } else { + message.value = null; + } + return message; + }, + toJSON(message: SimpleWithMap_EntitiesByIdEntry): unknown { + const obj: any = {}; + obj.key = message.key || 0; + obj.value = message.value ? Entity.toJSON(message.value) : null; + return obj; + }, +}; + +export const SimpleWithMap_NameLookupEntry = { + encode(message: SimpleWithMap_NameLookupEntry, writer: Writer = Writer.create()): Writer { + writer.uint32(10).string(message.key); + writer.uint32(18).string(message.value); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): SimpleWithMap_NameLookupEntry { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseSimpleWithMap_NameLookupEntry) as SimpleWithMap_NameLookupEntry; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = reader.string(); + break; + case 2: + message.value = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): SimpleWithMap_NameLookupEntry { + const message = Object.create(baseSimpleWithMap_NameLookupEntry) as SimpleWithMap_NameLookupEntry; + if (object.key !== undefined && object.key !== null) { + message.key = String(object.key); + } else { + message.key = ""; + } + if (object.value !== undefined && object.value !== null) { + message.value = String(object.value); + } else { + message.value = ""; + } + return message; + }, + fromPartial(object: DeepPartial): SimpleWithMap_NameLookupEntry { + const message = Object.create(baseSimpleWithMap_NameLookupEntry) as SimpleWithMap_NameLookupEntry; + if (object.key !== undefined && object.key !== null) { + message.key = object.key; + } else { + message.key = ""; + } + if (object.value !== undefined && object.value !== null) { + message.value = object.value; + } else { + message.value = ""; + } + return message; + }, + toJSON(message: SimpleWithMap_NameLookupEntry): unknown { + const obj: any = {}; + obj.key = message.key || ""; + obj.value = message.value || ""; + return obj; + }, +}; + +export const SimpleWithMap_IntLookupEntry = { + encode(message: SimpleWithMap_IntLookupEntry, writer: Writer = Writer.create()): Writer { + writer.uint32(8).int32(message.key); + writer.uint32(16).int32(message.value); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): SimpleWithMap_IntLookupEntry { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseSimpleWithMap_IntLookupEntry) as SimpleWithMap_IntLookupEntry; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = reader.int32(); + break; + case 2: + message.value = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): SimpleWithMap_IntLookupEntry { + const message = Object.create(baseSimpleWithMap_IntLookupEntry) as SimpleWithMap_IntLookupEntry; + if (object.key !== undefined && object.key !== null) { + message.key = Number(object.key); + } else { + message.key = 0; + } + if (object.value !== undefined && object.value !== null) { + message.value = Number(object.value); + } else { + message.value = 0; + } + return message; + }, + fromPartial(object: DeepPartial): SimpleWithMap_IntLookupEntry { + const message = Object.create(baseSimpleWithMap_IntLookupEntry) as SimpleWithMap_IntLookupEntry; + if (object.key !== undefined && object.key !== null) { + message.key = object.key; + } else { + message.key = 0; + } + if (object.value !== undefined && object.value !== null) { + message.value = object.value; + } else { + message.value = 0; + } + return message; + }, + toJSON(message: SimpleWithMap_IntLookupEntry): unknown { + const obj: any = {}; + obj.key = message.key || 0; + obj.value = message.value || 0; + return obj; + }, +}; + +export const SimpleWithSnakeCaseMap = { + encode(message: SimpleWithSnakeCaseMap, writer: Writer = Writer.create()): Writer { + Object.entries(message.entitiesById).forEach(([key, value]) => { + SimpleWithSnakeCaseMap_EntitiesByIdEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim(); + }) + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): SimpleWithSnakeCaseMap { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseSimpleWithSnakeCaseMap) as SimpleWithSnakeCaseMap; + message.entitiesById = {}; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + const entry1 = SimpleWithSnakeCaseMap_EntitiesByIdEntry.decode(reader, reader.uint32()); + if (entry1.value) { + message.entitiesById[entry1.key] = entry1.value; + } + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): SimpleWithSnakeCaseMap { + const message = Object.create(baseSimpleWithSnakeCaseMap) as SimpleWithSnakeCaseMap; + message.entitiesById = {}; + if (object.entitiesById !== undefined && object.entitiesById !== null) { + Object.entries(object.entitiesById).forEach(([key, value]) => { + message.entitiesById[Number(key)] = Entity.fromJSON(value); + }) + } + return message; + }, + fromPartial(object: DeepPartial): SimpleWithSnakeCaseMap { + const message = Object.create(baseSimpleWithSnakeCaseMap) as SimpleWithSnakeCaseMap; + message.entitiesById = {}; + if (object.entitiesById !== undefined && object.entitiesById !== null) { + Object.entries(object.entitiesById).forEach(([key, value]) => { + if (value) { + message.entitiesById[Number(key)] = Entity.fromPartial(value); + } + }) + } + return message; + }, + toJSON(message: SimpleWithSnakeCaseMap): unknown { + const obj: any = {}; + obj.entitiesById = message.entitiesById || null; + return obj; + }, +}; + +export const SimpleWithSnakeCaseMap_EntitiesByIdEntry = { + encode(message: SimpleWithSnakeCaseMap_EntitiesByIdEntry, writer: Writer = Writer.create()): Writer { + writer.uint32(8).int32(message.key); + if (message.value !== undefined && message.value !== null) { + Entity.encode(message.value, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): SimpleWithSnakeCaseMap_EntitiesByIdEntry { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseSimpleWithSnakeCaseMap_EntitiesByIdEntry) as SimpleWithSnakeCaseMap_EntitiesByIdEntry; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = reader.int32(); + break; + case 2: + message.value = Entity.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): SimpleWithSnakeCaseMap_EntitiesByIdEntry { + const message = Object.create(baseSimpleWithSnakeCaseMap_EntitiesByIdEntry) as SimpleWithSnakeCaseMap_EntitiesByIdEntry; + if (object.key !== undefined && object.key !== null) { + message.key = Number(object.key); + } else { + message.key = 0; + } + if (object.value !== undefined && object.value !== null) { + message.value = Entity.fromJSON(object.value); + } else { + message.value = null; + } + return message; + }, + fromPartial(object: DeepPartial): SimpleWithSnakeCaseMap_EntitiesByIdEntry { + const message = Object.create(baseSimpleWithSnakeCaseMap_EntitiesByIdEntry) as SimpleWithSnakeCaseMap_EntitiesByIdEntry; + if (object.key !== undefined && object.key !== null) { + message.key = object.key; + } else { + message.key = 0; + } + if (object.value !== undefined && object.value !== null) { + message.value = Entity.fromPartial(object.value); + } else { + message.value = null; + } + return message; + }, + toJSON(message: SimpleWithSnakeCaseMap_EntitiesByIdEntry): unknown { + const obj: any = {}; + obj.key = message.key || 0; + obj.value = message.value ? Entity.toJSON(message.value) : null; + return obj; + }, +}; + +export const PingRequest = { + encode(message: PingRequest, writer: Writer = Writer.create()): Writer { + writer.uint32(10).string(message.input); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): PingRequest { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(basePingRequest) as PingRequest; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.input = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): PingRequest { + const message = Object.create(basePingRequest) as PingRequest; + if (object.input !== undefined && object.input !== null) { + message.input = String(object.input); + } else { + message.input = ""; + } + return message; + }, + fromPartial(object: DeepPartial): PingRequest { + const message = Object.create(basePingRequest) as PingRequest; + if (object.input !== undefined && object.input !== null) { + message.input = object.input; + } else { + message.input = ""; + } + return message; + }, + toJSON(message: PingRequest): unknown { + const obj: any = {}; + obj.input = message.input || ""; + return obj; + }, +}; + +export const PingResponse = { + encode(message: PingResponse, writer: Writer = Writer.create()): Writer { + writer.uint32(10).string(message.output); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): PingResponse { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(basePingResponse) as PingResponse; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.output = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): PingResponse { + const message = Object.create(basePingResponse) as PingResponse; + if (object.output !== undefined && object.output !== null) { + message.output = String(object.output); + } else { + message.output = ""; + } + return message; + }, + fromPartial(object: DeepPartial): PingResponse { + const message = Object.create(basePingResponse) as PingResponse; + if (object.output !== undefined && object.output !== null) { + message.output = object.output; + } else { + message.output = ""; + } + return message; + }, + toJSON(message: PingResponse): unknown { + const obj: any = {}; + obj.output = message.output || ""; + return obj; + }, +}; + +export const Numbers = { + encode(message: Numbers, writer: Writer = Writer.create()): Writer { + writer.uint32(9).double(message.double); + writer.uint32(21).float(message.float); + writer.uint32(24).int32(message.int32); + writer.uint32(32).int64(message.int64); + writer.uint32(40).uint32(message.uint32); + writer.uint32(48).uint64(message.uint64); + writer.uint32(56).sint32(message.sint32); + writer.uint32(64).sint64(message.sint64); + writer.uint32(77).fixed32(message.fixed32); + writer.uint32(81).fixed64(message.fixed64); + writer.uint32(93).sfixed32(message.sfixed32); + writer.uint32(97).sfixed64(message.sfixed64); + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): Numbers { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseNumbers) as Numbers; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.double = reader.double(); + break; + case 2: + message.float = reader.float(); + break; + case 3: + message.int32 = reader.int32(); + break; + case 4: + message.int64 = longToNumber(reader.int64() as Long); + break; + case 5: + message.uint32 = reader.uint32(); + break; + case 6: + message.uint64 = longToNumber(reader.uint64() as Long); + break; + case 7: + message.sint32 = reader.sint32(); + break; + case 8: + message.sint64 = longToNumber(reader.sint64() as Long); + break; + case 9: + message.fixed32 = reader.fixed32(); + break; + case 10: + message.fixed64 = longToNumber(reader.fixed64() as Long); + break; + case 11: + message.sfixed32 = reader.sfixed32(); + break; + case 12: + message.sfixed64 = longToNumber(reader.sfixed64() as Long); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): Numbers { + const message = Object.create(baseNumbers) as Numbers; + if (object.double !== undefined && object.double !== null) { + message.double = Number(object.double); + } else { + message.double = 0; + } + if (object.float !== undefined && object.float !== null) { + message.float = Number(object.float); + } else { + message.float = 0; + } + if (object.int32 !== undefined && object.int32 !== null) { + message.int32 = Number(object.int32); + } else { + message.int32 = 0; + } + if (object.int64 !== undefined && object.int64 !== null) { + message.int64 = Number(object.int64); + } else { + message.int64 = 0; + } + if (object.uint32 !== undefined && object.uint32 !== null) { + message.uint32 = Number(object.uint32); + } else { + message.uint32 = 0; + } + if (object.uint64 !== undefined && object.uint64 !== null) { + message.uint64 = Number(object.uint64); + } else { + message.uint64 = 0; + } + if (object.sint32 !== undefined && object.sint32 !== null) { + message.sint32 = Number(object.sint32); + } else { + message.sint32 = 0; + } + if (object.sint64 !== undefined && object.sint64 !== null) { + message.sint64 = Number(object.sint64); + } else { + message.sint64 = 0; + } + if (object.fixed32 !== undefined && object.fixed32 !== null) { + message.fixed32 = Number(object.fixed32); + } else { + message.fixed32 = 0; + } + if (object.fixed64 !== undefined && object.fixed64 !== null) { + message.fixed64 = Number(object.fixed64); + } else { + message.fixed64 = 0; + } + if (object.sfixed32 !== undefined && object.sfixed32 !== null) { + message.sfixed32 = Number(object.sfixed32); + } else { + message.sfixed32 = 0; + } + if (object.sfixed64 !== undefined && object.sfixed64 !== null) { + message.sfixed64 = Number(object.sfixed64); + } else { + message.sfixed64 = 0; + } + return message; + }, + fromPartial(object: DeepPartial): Numbers { + const message = Object.create(baseNumbers) as Numbers; + if (object.double !== undefined && object.double !== null) { + message.double = object.double; + } else { + message.double = 0; + } + if (object.float !== undefined && object.float !== null) { + message.float = object.float; + } else { + message.float = 0; + } + if (object.int32 !== undefined && object.int32 !== null) { + message.int32 = object.int32; + } else { + message.int32 = 0; + } + if (object.int64 !== undefined && object.int64 !== null) { + message.int64 = object.int64; + } else { + message.int64 = 0; + } + if (object.uint32 !== undefined && object.uint32 !== null) { + message.uint32 = object.uint32; + } else { + message.uint32 = 0; + } + if (object.uint64 !== undefined && object.uint64 !== null) { + message.uint64 = object.uint64; + } else { + message.uint64 = 0; + } + if (object.sint32 !== undefined && object.sint32 !== null) { + message.sint32 = object.sint32; + } else { + message.sint32 = 0; + } + if (object.sint64 !== undefined && object.sint64 !== null) { + message.sint64 = object.sint64; + } else { + message.sint64 = 0; + } + if (object.fixed32 !== undefined && object.fixed32 !== null) { + message.fixed32 = object.fixed32; + } else { + message.fixed32 = 0; + } + if (object.fixed64 !== undefined && object.fixed64 !== null) { + message.fixed64 = object.fixed64; + } else { + message.fixed64 = 0; + } + if (object.sfixed32 !== undefined && object.sfixed32 !== null) { + message.sfixed32 = object.sfixed32; + } else { + message.sfixed32 = 0; + } + if (object.sfixed64 !== undefined && object.sfixed64 !== null) { + message.sfixed64 = object.sfixed64; + } else { + message.sfixed64 = 0; + } + return message; + }, + toJSON(message: Numbers): unknown { + const obj: any = {}; + obj.double = message.double || 0; + obj.float = message.float || 0; + obj.int32 = message.int32 || 0; + obj.int64 = message.int64 || 0; + obj.uint32 = message.uint32 || 0; + obj.uint64 = message.uint64 || 0; + obj.sint32 = message.sint32 || 0; + obj.sint64 = message.sint64 || 0; + obj.fixed32 = message.fixed32 || 0; + obj.fixed64 = message.fixed64 || 0; + obj.sfixed32 = message.sfixed32 || 0; + obj.sfixed64 = message.sfixed64 || 0; + return obj; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | undefined; +type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; \ No newline at end of file diff --git a/integration/simple-nullable-optionals/thing.bin b/integration/simple-nullable-optionals/thing.bin new file mode 100644 index 0000000000000000000000000000000000000000..ceb8bbad46661ea3e2723b17eda6054371b592ca GIT binary patch literal 6386 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 T2*6(W#r14dz^MFbW`zF${nw|} literal 0 HcmV?d00001 diff --git a/integration/simple-nullable-optionals/thing.proto b/integration/simple-nullable-optionals/thing.proto new file mode 100644 index 000000000..4742517f8 --- /dev/null +++ b/integration/simple-nullable-optionals/thing.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; +import "google/protobuf/timestamp.proto"; +package simple; + +message ImportedThing { + google.protobuf.Timestamp created_at = 1; +} \ No newline at end of file diff --git a/integration/simple-nullable-optionals/thing.ts b/integration/simple-nullable-optionals/thing.ts new file mode 100644 index 000000000..807e3d7bf --- /dev/null +++ b/integration/simple-nullable-optionals/thing.ts @@ -0,0 +1,92 @@ +import { Timestamp } from './google/protobuf/timestamp'; +import { Writer, Reader } from 'protobufjs/minimal'; + + +export interface ImportedThing { + createdAt?: Date | null; +} + +const baseImportedThing: object = { +}; + +function fromJsonTimestamp(o: any): Date { + if (o instanceof Date) { + return o; + } else if (typeof o === "string") { + return new Date(o); + } else { + return fromTimestamp(Timestamp.fromJSON(o)); + } +} + +function toTimestamp(date: Date): Timestamp { + const seconds = date.getTime() / 1_000; + const nanos = (date.getTime() % 1_000) * 1_000_000; + return { seconds, nanos }; +} + +function fromTimestamp(t: Timestamp): Date { + let millis = t.seconds * 1_000; + millis += t.nanos / 1_000_000; + return new Date(millis); +} + +export const ImportedThing = { + encode(message: ImportedThing, writer: Writer = Writer.create()): Writer { + if (message.createdAt !== undefined && message.createdAt !== null) { + Timestamp.encode(toTimestamp(message.createdAt), writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + decode(input: Uint8Array | Reader, length?: number): ImportedThing { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = Object.create(baseImportedThing) as ImportedThing; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.createdAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + fromJSON(object: any): ImportedThing { + const message = Object.create(baseImportedThing) as ImportedThing; + if (object.createdAt !== undefined && object.createdAt !== null) { + message.createdAt = fromJsonTimestamp(object.createdAt); + } else { + message.createdAt = null; + } + return message; + }, + fromPartial(object: DeepPartial): ImportedThing { + const message = Object.create(baseImportedThing) as ImportedThing; + if (object.createdAt !== undefined && object.createdAt !== null) { + message.createdAt = object.createdAt; + } else { + message.createdAt = null; + } + return message; + }, + toJSON(message: ImportedThing): unknown { + const obj: any = {}; + obj.createdAt = (message.createdAt !== undefined && message.createdAt !== null) ? message.createdAt.toISOString() : null; + return obj; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | undefined; +type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; \ No newline at end of file diff --git a/integration/simple-snake/google/protobuf/wrappers.ts b/integration/simple-snake/google/protobuf/wrappers.ts index 3b08d0f56..d02d409ad 100644 --- a/integration/simple-snake/google/protobuf/wrappers.ts +++ b/integration/simple-snake/google/protobuf/wrappers.ts @@ -567,7 +567,7 @@ export const BytesValue = { }, toJSON(message: BytesValue): unknown { const obj: any = {}; - obj.value = message.value !== undefined ? base64FromBytes(message.value) : undefined; + obj.value = (message.value !== undefined && message.value !== null) ? base64FromBytes(message.value) : undefined; return obj; }, }; diff --git a/integration/simple-snake/import_dir/thing.ts b/integration/simple-snake/import_dir/thing.ts index 0ea261aea..b073efad7 100644 --- a/integration/simple-snake/import_dir/thing.ts +++ b/integration/simple-snake/import_dir/thing.ts @@ -76,7 +76,7 @@ export const ImportedThing = { }, toJSON(message: ImportedThing): unknown { const obj: any = {}; - obj.created_at = message.created_at !== undefined ? message.created_at.toISOString() : null; + obj.created_at = (message.created_at !== undefined && message.created_at !== null) ? message.created_at.toISOString() : null; return obj; }, }; diff --git a/integration/simple-snake/simple.ts b/integration/simple-snake/simple.ts index 537ec82b8..c9c8b2f8e 100644 --- a/integration/simple-snake/simple.ts +++ b/integration/simple-snake/simple.ts @@ -613,7 +613,7 @@ export const Simple = { const obj: any = {}; obj.name = message.name || ""; obj.age = message.age || 0; - obj.created_at = message.created_at !== undefined ? message.created_at.toISOString() : null; + obj.created_at = (message.created_at !== undefined && message.created_at !== null) ? message.created_at.toISOString() : null; obj.child = message.child ? Child.toJSON(message.child) : undefined; obj.state = StateEnum.toJSON(message.state); if (message.grand_children) { diff --git a/integration/simple/google/protobuf/wrappers.ts b/integration/simple/google/protobuf/wrappers.ts index 3b08d0f56..d02d409ad 100644 --- a/integration/simple/google/protobuf/wrappers.ts +++ b/integration/simple/google/protobuf/wrappers.ts @@ -567,7 +567,7 @@ export const BytesValue = { }, toJSON(message: BytesValue): unknown { const obj: any = {}; - obj.value = message.value !== undefined ? base64FromBytes(message.value) : undefined; + obj.value = (message.value !== undefined && message.value !== null) ? base64FromBytes(message.value) : undefined; return obj; }, }; diff --git a/integration/simple/import_dir/thing.ts b/integration/simple/import_dir/thing.ts index 49903aa8b..eb8ac638c 100644 --- a/integration/simple/import_dir/thing.ts +++ b/integration/simple/import_dir/thing.ts @@ -76,7 +76,7 @@ export const ImportedThing = { }, toJSON(message: ImportedThing): unknown { const obj: any = {}; - obj.createdAt = message.createdAt !== undefined ? message.createdAt.toISOString() : null; + obj.createdAt = (message.createdAt !== undefined && message.createdAt !== null) ? message.createdAt.toISOString() : null; return obj; }, }; diff --git a/integration/simple/simple.ts b/integration/simple/simple.ts index ba57df686..474ecc093 100644 --- a/integration/simple/simple.ts +++ b/integration/simple/simple.ts @@ -640,7 +640,7 @@ export const Simple = { const obj: any = {}; obj.name = message.name || ""; obj.age = message.age || 0; - obj.createdAt = message.createdAt !== undefined ? message.createdAt.toISOString() : null; + obj.createdAt = (message.createdAt !== undefined && message.createdAt !== null) ? message.createdAt.toISOString() : null; obj.child = message.child ? Child.toJSON(message.child) : undefined; obj.state = StateEnum.toJSON(message.state); if (message.grandChildren) { @@ -665,7 +665,7 @@ export const Simple = { } obj.thing = message.thing ? ImportedThing.toJSON(message.thing) : undefined; if (message.blobs) { - obj.blobs = message.blobs.map(e => e !== undefined ? base64FromBytes(e) : undefined); + obj.blobs = message.blobs.map(e => (e !== undefined && e !== null) ? base64FromBytes(e) : undefined); } else { obj.blobs = []; } diff --git a/src/main.ts b/src/main.ts index d12f81e6e..6ea3c46a3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -23,6 +23,7 @@ import { isLong, isMapType, isMessage, + isOptional, isPrimitive, isRepeated, isTimestamp, @@ -33,7 +34,8 @@ import { toReaderCall, toTypeName, TypeMap, - isEmptyType + isEmptyType, + valueTypeName, } from './types'; import { asSequence } from 'sequency'; import SourceInfo, { Fields } from './sourceInfo'; @@ -58,6 +60,7 @@ export type Options = { useContext: boolean; snakeToCamel: boolean; forceLong: LongOption; + nullableOptionals: boolean; outputEncodeMethods: boolean; outputJsonMethods: boolean; outputClientImpl: boolean; @@ -424,9 +427,20 @@ function generateInterfaceDeclaration( let index = 0; for (const fieldDesc of messageDesc.field) { + let typeName = toTypeName(typeMap, messageDesc, fieldDesc, options) + + // When nullableOptionals=true, message fields and fields within a oneof + // clause are translated into optional properties and their values are + // nullable. + let optional = options.nullableOptionals && isOptional(fieldDesc) && !isRepeated(fieldDesc) + if (optional) { + typeName = TypeNames.unionType(typeName, TypeNames.NULL) + } + let prop = PropertySpec.create( maybeSnakeToCamel(fieldDesc.name, options), - toTypeName(typeMap, messageDesc, fieldDesc, options) + typeName, + optional ); const info = sourceInfo.lookup(Fields.message.field, index++); @@ -442,7 +456,7 @@ function generateBaseInstance(typeMap: TypeMap, fullName: string, messageDesc: D let baseMessage = PropertySpec.create('base' + fullName, TypeNames.anyType('object')).addModifiers(Modifier.CONST); let initialValue = CodeBlock.empty().beginHash(); asSequence(messageDesc.field) - .filterNot(isWithinOneOf) + .filterNot(options.nullableOptionals ? isOptional : isWithinOneOf) .forEach(field => { initialValue = initialValue.addHashEntry( maybeSnakeToCamel(field.name, options), @@ -692,7 +706,7 @@ function generateEncode( .endControlFlow() .addStatement('writer.ldelim()'); } - } else if (isWithinOneOf(field) || isMessage(field)) { + } else if (isOptional(field)) { func = func .beginControlFlow( 'if (message.%L !== undefined && message.%L !== %L)', @@ -760,8 +774,7 @@ function generateFromJson( } else if (isTimestamp(field)) { return CodeBlock.of('fromJsonTimestamp(%L)', from); } else if (isValueType(field)) { - const cstr = capitalize((basicTypeName(typeMap, field, options, false) as Union).typeChoices[0].toString()); - return CodeBlock.of('%L(%L)', cstr, from); + return CodeBlock.of('%L(%L)', capitalize(valueTypeName(field).toString()), from); } else if (isMessage(field)) { if (isRepeated(field) && isMapType(typeMap, messageDesc, field, options)) { const valueType = (typeMap.get(field.typeName)![2] as DescriptorProto).field[1]; @@ -840,7 +853,12 @@ function generateToJson( if (isEnum(field)) { return CodeBlock.of('%T.toJSON(%L)', basicTypeName(typeMap, field, options), from); } else if (isTimestamp(field)) { - return CodeBlock.of('%L !== undefined ? %L.toISOString() : null', from, from); + return CodeBlock.of( + '(%L !== undefined && %L !== null) ? %L.toISOString() : null', + from, + from, + from, + ); } else if (isMessage(field) && !isValueType(field) && !isMapType(typeMap, messageDesc, field, options)) { return CodeBlock.of( '%L ? %T.toJSON(%L) : %L', @@ -851,7 +869,8 @@ function generateToJson( ); } else if (isBytes(field)) { return CodeBlock.of( - '%L !== undefined ? base64FromBytes(%L) : %L', + '(%L !== undefined && %L !== null) ? base64FromBytes(%L) : %L', + from, from, from, isWithinOneOf(field) ? 'undefined' : defaultValue(typeMap, field, options) diff --git a/src/types.ts b/src/types.ts index 74d6cbab6..fd256bedf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -87,7 +87,7 @@ export function basicTypeName(typeMap: TypeMap, field: FieldDescriptorProto, opt return TypeNames.anyType('Uint8Array'); case FieldDescriptorProto.Type.TYPE_MESSAGE: case FieldDescriptorProto.Type.TYPE_ENUM: - return messageToTypeName(typeMap, field.typeName, keepValueType); + return messageToTypeName(typeMap, field.typeName, options, keepValueType); default: return TypeNames.anyType(field.typeName); } @@ -161,6 +161,9 @@ export function packedType(type: FieldDescriptorProto.Type): number | undefined } export function defaultValue(typeMap: TypeMap, field: FieldDescriptorProto, options: Options): any { + if (options.nullableOptionals && isOptional(field)) { + return 'null'; + } switch (field.type) { case FieldDescriptorProto.Type.TYPE_DOUBLE: case FieldDescriptorProto.Type.TYPE_FLOAT: @@ -260,16 +263,20 @@ export function isMapType(typeMap: TypeMap, messageDesc: DescriptorProto, field: return detectMapType(typeMap, messageDesc, field, options) !== undefined; } +export function isOptional(field: FieldDescriptorProto): boolean { + return isWithinOneOf(field) || isMessage(field); +} + const valueTypes: { [key: string]: TypeName } = { - '.google.protobuf.StringValue': TypeNames.unionType(TypeNames.STRING, TypeNames.UNDEFINED), - '.google.protobuf.Int32Value': TypeNames.unionType(TypeNames.NUMBER, TypeNames.UNDEFINED), - '.google.protobuf.Int64Value': TypeNames.unionType(TypeNames.NUMBER, TypeNames.UNDEFINED), - '.google.protobuf.UInt32Value': TypeNames.unionType(TypeNames.NUMBER, TypeNames.UNDEFINED), - '.google.protobuf.UInt64Value': TypeNames.unionType(TypeNames.NUMBER, TypeNames.UNDEFINED), - '.google.protobuf.BoolValue': TypeNames.unionType(TypeNames.BOOLEAN, TypeNames.UNDEFINED), - '.google.protobuf.DoubleValue': TypeNames.unionType(TypeNames.NUMBER, TypeNames.UNDEFINED), - '.google.protobuf.FloatValue': TypeNames.unionType(TypeNames.NUMBER, TypeNames.UNDEFINED), - '.google.protobuf.BytesValue': TypeNames.unionType(TypeNames.anyType('Uint8Array'), TypeNames.UNDEFINED) + '.google.protobuf.StringValue': TypeNames.STRING, + '.google.protobuf.Int32Value': TypeNames.NUMBER, + '.google.protobuf.Int64Value': TypeNames.NUMBER, + '.google.protobuf.UInt32Value': TypeNames.NUMBER, + '.google.protobuf.UInt64Value': TypeNames.NUMBER, + '.google.protobuf.BoolValue': TypeNames.BOOLEAN, + '.google.protobuf.DoubleValue': TypeNames.NUMBER, + '.google.protobuf.FloatValue': TypeNames.NUMBER, + '.google.protobuf.BytesValue': TypeNames.anyType('Uint8Array'), }; const mappedTypes: { [key: string]: TypeName } = { @@ -288,11 +295,26 @@ export function isEmptyType(typeName: string): boolean { return typeName === '.google.protobuf.Empty'; } +export function valueTypeName(field: FieldDescriptorProto): TypeName { + if (!isValueType(field)) { + throw new Error('Type is not a valueType: ' + field.typeName) + } + return valueTypes[field.typeName] +} + /** Maps `.some_proto_namespace.Message` to a TypeName. */ -export function messageToTypeName(typeMap: TypeMap, protoType: string, keepValueType: boolean = false): TypeName { +export function messageToTypeName(typeMap: TypeMap, protoType: string, options: (Options | null) = null, keepValueType: boolean = false): TypeName { // Watch for the wrapper types `.google.protobuf.StringValue` and map to `string | undefined` if (!keepValueType && protoType in valueTypes) { - return valueTypes[protoType]; + let typeName = valueTypes[protoType]; + if (!options || !options.nullableOptionals) { + // When nullableOptionals=false (the default), we union the type with + // undefined to indicate the value is optional. If nullableOptionals=true, + // all message types are already optional properties and their values are + // nullable, so there's no need for it here. + typeName = TypeNames.unionType(typeName, TypeNames.UNDEFINED); + } + return typeName; } // Look for other special prototypes like Timestamp that aren't technically wrapper types if (!keepValueType && protoType in mappedTypes) { @@ -318,7 +340,15 @@ export function toTypeName(typeMap: TypeMap, messageDesc: DescriptorProto, field } else { type = TypeNames.arrayType(type); } - } else if ((isWithinOneOf(field) || isMessage(field)) && !isValueType(field)) { + } else if (isOptional(field) && !isValueType(field) && !options.nullableOptionals) { + // When nullableOptionals=false (the default), message fields and fields + // within a oneof clause need to be unioned with undefined to indicate the + // value is optional. One exception is google.protobuf.*Value types, which + // are already unioned to undefined in messageToTypeName. + // + // When nullableOptionals=true, message fields and fields within a oneof + // clause are already optional and their values are nullable, so no need + // for the type union here. type = TypeNames.unionType(type, TypeNames.UNDEFINED); } return type; diff --git a/src/utils.ts b/src/utils.ts index d373c3d6e..2fad36a8b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -40,6 +40,7 @@ export function optionsFromParameter(parameter: string): Options { useContext: false, snakeToCamel: true, forceLong: LongOption.NUMBER, + nullableOptionals: false, lowerCaseServiceMethods: false, outputEncodeMethods: true, outputJsonMethods: true, @@ -62,6 +63,9 @@ export function optionsFromParameter(parameter: string): Options { if (parameter.includes('forceLong=string')) { options.forceLong = LongOption.STRING; } + if (parameter.includes('nullableOptionals=true')) { + options.nullableOptionals = true; + } if (parameter.includes('lowerCaseServiceMethods=true')) { options.lowerCaseServiceMethods = true; }