From e838e3801e0ef5e8b5a14ead7d7dfc0ad3532cf1 Mon Sep 17 00:00:00 2001 From: Koen van Staveren Date: Wed, 28 Feb 2024 18:20:47 +0100 Subject: [PATCH 1/4] fix: typescript errors for struct with optional=all (#1008) Fixes #578 Due to the use of the `.foreach` function TS has no context of the clue that `struct.fields` is definently not undefined. --- integration/grpc-js/google/protobuf/struct.ts | 9 +- .../nestjs-simple/google/protobuf/struct.ts | 9 +- .../nice-grpc/google/protobuf/struct.ts | 9 +- .../google/protobuf/struct.ts | 9 +- .../oneof-unions/google/protobuf/struct.ts | 9 +- integration/simple-esmodule-interop/empty.bin | Bin 0 -> 75 bytes .../simple-snake/google/protobuf/struct.ts | 9 +- .../google/protobuf/struct.ts | 9 +- .../google/protobuf/struct.ts | 9 +- .../static-only/google/protobuf/struct.ts | 9 +- integration/struct/google/protobuf/struct.ts | 9 +- .../google/protobuf/struct.ts | 9 +- .../type-registry/google/protobuf/struct.ts | 9 +- .../use-map-type/google/protobuf/struct.ts | 13 +- .../google/protobuf/struct.ts | 9 +- .../google/protobuf/struct.ts | 550 ++++++++++++++++++ integration/use-optionals-all/test.bin | Bin 9257 -> 13884 bytes integration/use-optionals-all/test.proto | 2 + integration/use-optionals-all/test.ts | 18 + .../google/protobuf/struct.ts | 548 +++++++++++++++++ .../use-optionals-no-undefined/test.bin | Bin 2755 -> 7382 bytes .../use-optionals-no-undefined/test.proto | 4 + .../use-optionals-no-undefined/test.ts | 17 + .../google/protobuf/struct.ts | 9 +- integration/value/google/protobuf/struct.ts | 9 +- src/generate-struct-wrappers.ts | 42 +- 26 files changed, 1250 insertions(+), 79 deletions(-) create mode 100644 integration/simple-esmodule-interop/empty.bin create mode 100644 integration/use-optionals-all/google/protobuf/struct.ts create mode 100644 integration/use-optionals-no-undefined/google/protobuf/struct.ts diff --git a/integration/grpc-js/google/protobuf/struct.ts b/integration/grpc-js/google/protobuf/struct.ts index 5760ebc59..849206f64 100644 --- a/integration/grpc-js/google/protobuf/struct.ts +++ b/integration/grpc-js/google/protobuf/struct.ts @@ -184,10 +184,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -195,9 +196,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/nestjs-simple/google/protobuf/struct.ts b/integration/nestjs-simple/google/protobuf/struct.ts index a3485c05d..de6125dfb 100644 --- a/integration/nestjs-simple/google/protobuf/struct.ts +++ b/integration/nestjs-simple/google/protobuf/struct.ts @@ -87,10 +87,11 @@ function createBaseStruct(): Struct { export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = Value.wrap(object[key]); - }); + } } return struct; }, @@ -98,9 +99,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = Value.unwrap(message.fields[key]); - }); + } } return object; }, diff --git a/integration/nice-grpc/google/protobuf/struct.ts b/integration/nice-grpc/google/protobuf/struct.ts index c14e39613..d773b780c 100644 --- a/integration/nice-grpc/google/protobuf/struct.ts +++ b/integration/nice-grpc/google/protobuf/struct.ts @@ -184,10 +184,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -195,9 +196,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/oneof-unions-snake/google/protobuf/struct.ts b/integration/oneof-unions-snake/google/protobuf/struct.ts index 4208674b9..76745053b 100644 --- a/integration/oneof-unions-snake/google/protobuf/struct.ts +++ b/integration/oneof-unions-snake/google/protobuf/struct.ts @@ -170,10 +170,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -181,9 +182,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/oneof-unions/google/protobuf/struct.ts b/integration/oneof-unions/google/protobuf/struct.ts index e7ee042b7..a12c690df 100644 --- a/integration/oneof-unions/google/protobuf/struct.ts +++ b/integration/oneof-unions/google/protobuf/struct.ts @@ -170,10 +170,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -181,9 +182,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/simple-esmodule-interop/empty.bin b/integration/simple-esmodule-interop/empty.bin new file mode 100644 index 0000000000000000000000000000000000000000..fda409fb6ec9e03971543fe7a63ed09b583cfc64 GIT binary patch literal 75 zcmd<$PR%VSsnjbd$}h>6;^1Hw5SCz6VyH6WLY5F>1*`Is;bIeFVPIh7 { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -195,9 +196,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/simple-string-enums/google/protobuf/struct.ts b/integration/simple-string-enums/google/protobuf/struct.ts index e866f6505..8f12e5f25 100644 --- a/integration/simple-string-enums/google/protobuf/struct.ts +++ b/integration/simple-string-enums/google/protobuf/struct.ts @@ -194,10 +194,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -205,9 +206,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/static-only-type-registry/google/protobuf/struct.ts b/integration/static-only-type-registry/google/protobuf/struct.ts index 6d44332d0..ca34a0f08 100644 --- a/integration/static-only-type-registry/google/protobuf/struct.ts +++ b/integration/static-only-type-registry/google/protobuf/struct.ts @@ -187,10 +187,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -198,9 +199,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/static-only/google/protobuf/struct.ts b/integration/static-only/google/protobuf/struct.ts index 4831a6f92..046a09e01 100644 --- a/integration/static-only/google/protobuf/struct.ts +++ b/integration/static-only/google/protobuf/struct.ts @@ -186,10 +186,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -197,9 +198,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/struct/google/protobuf/struct.ts b/integration/struct/google/protobuf/struct.ts index 5760ebc59..849206f64 100644 --- a/integration/struct/google/protobuf/struct.ts +++ b/integration/struct/google/protobuf/struct.ts @@ -184,10 +184,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -195,9 +196,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/type-annotations/google/protobuf/struct.ts b/integration/type-annotations/google/protobuf/struct.ts index e1cd21542..de5edcd11 100644 --- a/integration/type-annotations/google/protobuf/struct.ts +++ b/integration/type-annotations/google/protobuf/struct.ts @@ -194,10 +194,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -205,9 +206,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/type-registry/google/protobuf/struct.ts b/integration/type-registry/google/protobuf/struct.ts index 122718b12..3f445b3de 100644 --- a/integration/type-registry/google/protobuf/struct.ts +++ b/integration/type-registry/google/protobuf/struct.ts @@ -195,10 +195,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -206,9 +207,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/use-map-type/google/protobuf/struct.ts b/integration/use-map-type/google/protobuf/struct.ts index ca709b109..1d5514fa1 100644 --- a/integration/use-map-type/google/protobuf/struct.ts +++ b/integration/use-map-type/google/protobuf/struct.ts @@ -181,19 +181,22 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields.set(key, object[key]); - }); + } } return struct; }, unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - [...message.fields.keys()].forEach((key) => { - object[key] = message.fields.get(key); - }); + if (message.fields) { + for (const key of message.fields.keys()) { + object[key] = message.fields.get(key); + } + } return object; }, }; diff --git a/integration/use-numeric-enum-json/google/protobuf/struct.ts b/integration/use-numeric-enum-json/google/protobuf/struct.ts index 276b617e8..956cb310e 100644 --- a/integration/use-numeric-enum-json/google/protobuf/struct.ts +++ b/integration/use-numeric-enum-json/google/protobuf/struct.ts @@ -184,10 +184,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -195,9 +196,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/use-optionals-all/google/protobuf/struct.ts b/integration/use-optionals-all/google/protobuf/struct.ts new file mode 100644 index 000000000..e465e0978 --- /dev/null +++ b/integration/use-optionals-all/google/protobuf/struct.ts @@ -0,0 +1,550 @@ +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; + +export const protobufPackage = "google.protobuf"; + +/** + * `NullValue` is a singleton enumeration to represent the null value for the + * `Value` type union. + * + * The JSON representation for `NullValue` is JSON `null`. + */ +export enum NullValue { + /** NULL_VALUE - Null value. */ + NULL_VALUE = 0, + UNRECOGNIZED = -1, +} + +export function nullValueFromJSON(object: any): NullValue { + switch (object) { + case 0: + case "NULL_VALUE": + return NullValue.NULL_VALUE; + case -1: + case "UNRECOGNIZED": + default: + return NullValue.UNRECOGNIZED; + } +} + +export function nullValueToJSON(object: NullValue): string { + switch (object) { + case NullValue.NULL_VALUE: + return "NULL_VALUE"; + case NullValue.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +/** + * `Struct` represents a structured data value, consisting of fields + * which map to dynamically typed values. In some languages, `Struct` + * might be supported by a native representation. For example, in + * scripting languages like JS a struct is represented as an + * object. The details of that representation are described together + * with the proto support for the language. + * + * The JSON representation for `Struct` is JSON object. + */ +export interface Struct { + /** Unordered map of dynamically typed values. */ + fields?: { [key: string]: any | undefined } | undefined; +} + +export interface Struct_FieldsEntry { + key: string; + value?: any | undefined; +} + +/** + * `Value` represents a dynamically typed value which can be either + * null, a number, a string, a boolean, a recursive struct value, or a + * list of values. A producer of value is expected to set one of these + * variants. Absence of any variant indicates an error. + * + * The JSON representation for `Value` is JSON value. + */ +export interface Value { + /** Represents a null value. */ + nullValue?: + | NullValue + | undefined; + /** Represents a double value. */ + numberValue?: + | number + | undefined; + /** Represents a string value. */ + stringValue?: + | string + | undefined; + /** Represents a boolean value. */ + boolValue?: + | boolean + | undefined; + /** Represents a structured value. */ + structValue?: + | { [key: string]: any } + | undefined; + /** Represents a repeated `Value`. */ + listValue?: Array | undefined; +} + +/** + * `ListValue` is a wrapper around a repeated field of values. + * + * The JSON representation for `ListValue` is JSON array. + */ +export interface ListValue { + /** Repeated field of dynamically typed values. */ + values?: any[] | undefined; +} + +function createBaseStruct(): Struct { + return { fields: {} }; +} + +export const Struct = { + encode(message: Struct, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + Object.entries(message.fields || {}).forEach(([key, value]) => { + if (value !== undefined) { + Struct_FieldsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim(); + } + }); + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Struct { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStruct(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + const entry1 = Struct_FieldsEntry.decode(reader, reader.uint32()); + if (entry1.value !== undefined) { + message.fields![entry1.key] = entry1.value; + } + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Struct { + return { + fields: isObject(object.fields) + ? Object.entries(object.fields).reduce<{ [key: string]: any | undefined }>((acc, [key, value]) => { + acc[key] = value as any | undefined; + return acc; + }, {}) + : {}, + }; + }, + + toJSON(message: Struct): unknown { + const obj: any = {}; + if (message.fields) { + const entries = Object.entries(message.fields); + if (entries.length > 0) { + obj.fields = {}; + entries.forEach(([k, v]) => { + obj.fields[k] = v; + }); + } + } + return obj; + }, + + create, I>>(base?: I): Struct { + return Struct.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Struct { + const message = createBaseStruct(); + message.fields = Object.entries(object.fields ?? {}).reduce<{ [key: string]: any | undefined }>( + (acc, [key, value]) => { + if (value !== undefined) { + acc[key] = value; + } + return acc; + }, + {}, + ); + return message; + }, + + wrap(object: { [key: string]: any } | undefined): Struct { + const struct = createBaseStruct(); + struct.fields ??= {}; + if (object !== undefined) { + for (const key of Object.keys(object)) { + struct.fields[key] = object[key]; + } + } + return struct; + }, + + unwrap(message: Struct): { [key: string]: any } { + const object: { [key: string]: any } = {}; + if (message.fields) { + for (const key of Object.keys(message.fields)) { + object[key] = message.fields[key]; + } + } + return object; + }, +}; + +function createBaseStruct_FieldsEntry(): Struct_FieldsEntry { + return { key: "", value: undefined }; +} + +export const Struct_FieldsEntry = { + encode(message: Struct_FieldsEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== undefined) { + Value.encode(Value.wrap(message.value), writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Struct_FieldsEntry { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStruct_FieldsEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.value = Value.unwrap(Value.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Struct_FieldsEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object?.value) ? object.value : undefined, + }; + }, + + toJSON(message: Struct_FieldsEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== undefined) { + obj.value = message.value; + } + return obj; + }, + + create, I>>(base?: I): Struct_FieldsEntry { + return Struct_FieldsEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Struct_FieldsEntry { + const message = createBaseStruct_FieldsEntry(); + message.key = object.key ?? ""; + message.value = object.value ?? undefined; + return message; + }, +}; + +function createBaseValue(): Value { + return { + nullValue: undefined, + numberValue: undefined, + stringValue: undefined, + boolValue: undefined, + structValue: undefined, + listValue: undefined, + }; +} + +export const Value = { + encode(message: Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.nullValue !== undefined) { + writer.uint32(8).int32(message.nullValue); + } + if (message.numberValue !== undefined) { + writer.uint32(17).double(message.numberValue); + } + if (message.stringValue !== undefined) { + writer.uint32(26).string(message.stringValue); + } + if (message.boolValue !== undefined) { + writer.uint32(32).bool(message.boolValue); + } + if (message.structValue !== undefined) { + Struct.encode(Struct.wrap(message.structValue), writer.uint32(42).fork()).ldelim(); + } + if (message.listValue !== undefined) { + ListValue.encode(ListValue.wrap(message.listValue), writer.uint32(50).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Value { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.nullValue = reader.int32() as any; + continue; + case 2: + if (tag !== 17) { + break; + } + + message.numberValue = reader.double(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.stringValue = reader.string(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.boolValue = reader.bool(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.structValue = Struct.unwrap(Struct.decode(reader, reader.uint32())); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.listValue = ListValue.unwrap(ListValue.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Value { + return { + nullValue: isSet(object.nullValue) ? nullValueFromJSON(object.nullValue) : undefined, + numberValue: isSet(object.numberValue) ? globalThis.Number(object.numberValue) : undefined, + stringValue: isSet(object.stringValue) ? globalThis.String(object.stringValue) : undefined, + boolValue: isSet(object.boolValue) ? globalThis.Boolean(object.boolValue) : undefined, + structValue: isObject(object.structValue) ? object.structValue : undefined, + listValue: globalThis.Array.isArray(object.listValue) ? [...object.listValue] : undefined, + }; + }, + + toJSON(message: Value): unknown { + const obj: any = {}; + if (message.nullValue !== undefined) { + obj.nullValue = nullValueToJSON(message.nullValue); + } + if (message.numberValue !== undefined) { + obj.numberValue = message.numberValue; + } + if (message.stringValue !== undefined) { + obj.stringValue = message.stringValue; + } + if (message.boolValue !== undefined) { + obj.boolValue = message.boolValue; + } + if (message.structValue !== undefined) { + obj.structValue = message.structValue; + } + if (message.listValue !== undefined) { + obj.listValue = message.listValue; + } + return obj; + }, + + create, I>>(base?: I): Value { + return Value.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Value { + const message = createBaseValue(); + message.nullValue = object.nullValue ?? undefined; + message.numberValue = object.numberValue ?? undefined; + message.stringValue = object.stringValue ?? undefined; + message.boolValue = object.boolValue ?? undefined; + message.structValue = object.structValue ?? undefined; + message.listValue = object.listValue ?? undefined; + return message; + }, + + wrap(value: any): Value { + const result = createBaseValue(); + if (value === null) { + result.nullValue = NullValue.NULL_VALUE; + } else if (typeof value === "boolean") { + result.boolValue = value; + } else if (typeof value === "number") { + result.numberValue = value; + } else if (typeof value === "string") { + result.stringValue = value; + } else if (globalThis.Array.isArray(value)) { + result.listValue = value; + } else if (typeof value === "object") { + result.structValue = value; + } else if (typeof value !== "undefined") { + throw new globalThis.Error("Unsupported any value type: " + typeof value); + } + return result; + }, + + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.stringValue !== undefined) { + return message.stringValue; + } else if (message?.numberValue !== undefined) { + return message.numberValue; + } else if (message?.boolValue !== undefined) { + return message.boolValue; + } else if (message?.structValue !== undefined) { + return message.structValue as any; + } else if (message?.listValue !== undefined) { + return message.listValue; + } else if (message?.nullValue !== undefined) { + return null; + } + return undefined; + }, +}; + +function createBaseListValue(): ListValue { + return { values: [] }; +} + +export const ListValue = { + encode(message: ListValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.values !== undefined && message.values.length !== 0) { + for (const v of message.values) { + Value.encode(Value.wrap(v!), writer.uint32(10).fork()).ldelim(); + } + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ListValue { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseListValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.values!.push(Value.unwrap(Value.decode(reader, reader.uint32()))); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ListValue { + return { values: globalThis.Array.isArray(object?.values) ? [...object.values] : [] }; + }, + + toJSON(message: ListValue): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + return obj; + }, + + create, I>>(base?: I): ListValue { + return ListValue.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ListValue { + const message = createBaseListValue(); + message.values = object.values?.map((e) => e) || []; + return message; + }, + + wrap(array: Array | undefined): ListValue { + const result = createBaseListValue(); + result.values = array ?? []; + return result; + }, + + unwrap(message: ListValue): Array { + if (message?.hasOwnProperty("values") && globalThis.Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/use-optionals-all/test.bin b/integration/use-optionals-all/test.bin index 9cf270203b0d484b0bba518a5720d92212049abc..18b318f74bd2e262acce919a58f4b7f03f4d9a34 100644 GIT binary patch delta 4545 zcmai%-)|dB6~}jH*Uoy@KgV|LtnFsvHa~8grlMRB+#jmaD(TH_ZYjx4AGqqh+1T5} z)bUztyO$uL6}%uJ@o<$Fgm^~WH$;(m;gSCVAtVHX{{y}=J3DPswdBb&bIxbZneUl1 zo3B6m^YHioYR3QV()Mu}9uI?C<0zblN3*B5Cevuvn{LQ4D&+nKZjEE!g`s z`*aWt`;(^0tu^=d)s0KW8;Rk@wzS$BO{23t<>Gyxy4!GPRxsXCRqtxjpfd zt<%hNe>e-81at1~`Pb-=#p+&S^d9+++5H2d899m2=x8<^evtqKw0Qp0b~S#8Nonzs zv6mATiTPEUAI(mWf=D@t9Zi_H$*eZQ(F{h%%E%I-3ZrB;FD=`Oy zN3(!^K=W|G2`fwnHty}lYuP1dr}!{W_(*Ixn54eh^PjXE@h&Fhs@O@a%E8?%wVw?} z{qFlTr!Ck9%{~{$mTLBiTQsN7j1?(;c(A+s#pBK0gRQc0H>TBIc)D~s^t)~n z`y)|NkIWz3`s$CX*(B$${&O?;>kiE_`-<`Ti0g{6Py+FJ^M}JTK0O-)keuBH-x={q z7@r1w=#P$P{&6r_*Sk}GIygR=@}q!HX5(=fP2qTShDb*KbnrZQ86AehNKN3Hqpf`&qLieN))B3{!`4qBFrXtIoQaf?%PeTb1_s^ zGnZnuK)r7}Dmn|)`?+c|iissi^9_1aVr^#aAXt78`%3$}7_W_Mv!mn1<*;^iyf(l) zh&KTvga?r85;%&3ae$iYrgK3C*#SIIhq-UWk>*gNIcJ#7p(hfz_fMnC=vytesDEGHFtBd=~esw6= zhj2yFXGHPsXNuILc1HHAgP2sj0vCR-Y9K;y-1MIF zL;WbLgujgZ@fi2skHQ(QkmwxheC6Y$@6<%(@+lf|{Km$65iQO9DDuyeRBTb3pE(u! z9?FJ@%lL$}y_=$J5F!{l(J4&#iB;TU32PH2GVog*cg1O_PnIWm`yU^wR_>0%8j z%@vBu4NDGT%Fx6~IND9LnAmZWK4uWnW1MnD`Jp^=WM26Z_~nS3j^LNeSJDyOa(qv8LpiRU@k^lsqUkP052&bJMEIgHmHlsU;}2 zYbz?00<-EmlmZ}i4JiOp*ZC9xsUx2_sm1~*bsb8PSq&|75tN3e6hUcdN)eQXmbnN> zL(5!Z7XC+6QI-H{YDftJH4})azY-`-EprK!rk1(PtOYG|8I%Q0DTA`0WiA7@s0maj&!kE?46b4F5Qy3sEEi(h8rDaB?SS<~y zGOMjsuG*--wx(1;X=|CQptQBjRZ!Ym<{Gm)I+Pkh>1ap|kdDr$21rNeQv;-{1)fC=?H#{%d4u!=9#rbZm6YsxGj6+YvMf+-y+M#JMuk{Z~ZdfdbGaetzRKUv6Xt; zS4b`$p?R<53(64eRlOv7{wlGRp-Dc$5Kl*fgxgwjkU)y(c#UL~B@t^*PW%Hb`tc2; OET1TWyWtel=ktGi-a?iD delta 1716 zcmX|?yN(nw5Jh|Jez@&^*wsBfuin|2-I;wMK|sm}@DF52WJrq;2~mf9z~pHy#1I1rnRsgrAY>!N0749@Xp)_{4YO3BbWEv0=}f#;6(}9; zV<#xx;Ub|O z09i4_VLz+$DE|IAP*!{_4wMz23Xxnhg+N&|g+N(z%mm1qV@Akw&5(}dhEwhU*)U}4 zK-qB29Vi=)xdUaxG4~`#9;F9mWJ(Xn$Y*API4>m!Zn)bxPMzQj}pG^ za1|AN+v9DxV*|;Zcos?nLfDB-RIu&FV^py1?jG=#iwy7jb#i-p*E9dFUuUNWIy=4V z2hqSXWnQ6n5Tz%0g;)4c)qY_pl#X2TkM~iep>c-BW1&JL#Lf9Y;PSQ`k%dO#YpT>G a{u&hS6MOn translations = 30; google.protobuf.Timestamp timestamp = 31; + google.protobuf.Struct struct = 32; } enum StateEnum { diff --git a/integration/use-optionals-all/test.ts b/integration/use-optionals-all/test.ts index bb2115609..5ddbea317 100644 --- a/integration/use-optionals-all/test.ts +++ b/integration/use-optionals-all/test.ts @@ -1,5 +1,6 @@ /* eslint-disable */ import * as _m0 from "protobufjs/minimal"; +import { Struct } from "./google/protobuf/struct"; import { Timestamp } from "./google/protobuf/timestamp"; import Long = require("long"); @@ -68,6 +69,7 @@ export interface OptionalsTest { optData?: Uint8Array | undefined; translations?: { [key: string]: string } | undefined; timestamp?: Date | undefined; + struct?: { [key: string]: any } | undefined; } export interface OptionalsTest_TranslationsEntry { @@ -103,6 +105,7 @@ function createBaseOptionalsTest(): OptionalsTest { optData: undefined, translations: {}, timestamp: undefined, + struct: undefined, }; } @@ -199,6 +202,9 @@ export const OptionalsTest = { if (message.timestamp !== undefined) { Timestamp.encode(toTimestamp(message.timestamp), writer.uint32(250).fork()).ldelim(); } + if (message.struct !== undefined) { + Struct.encode(Struct.wrap(message.struct), writer.uint32(258).fork()).ldelim(); + } return writer; }, @@ -413,6 +419,13 @@ export const OptionalsTest = { message.timestamp = fromTimestamp(Timestamp.decode(reader, reader.uint32())); continue; + case 32: + if (tag !== 258) { + break; + } + + message.struct = Struct.unwrap(Struct.decode(reader, reader.uint32())); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -456,6 +469,7 @@ export const OptionalsTest = { }, {}) : {}, timestamp: isSet(object.timestamp) ? fromJsonTimestamp(object.timestamp) : undefined, + struct: isObject(object.struct) ? object.struct : undefined, }; }, @@ -536,6 +550,9 @@ export const OptionalsTest = { if (message.timestamp !== undefined) { obj.timestamp = message.timestamp.toISOString(); } + if (message.struct !== undefined) { + obj.struct = message.struct; + } return obj; }, @@ -577,6 +594,7 @@ export const OptionalsTest = { {}, ); message.timestamp = object.timestamp ?? undefined; + message.struct = object.struct ?? undefined; return message; }, }; diff --git a/integration/use-optionals-no-undefined/google/protobuf/struct.ts b/integration/use-optionals-no-undefined/google/protobuf/struct.ts new file mode 100644 index 000000000..6b72c2577 --- /dev/null +++ b/integration/use-optionals-no-undefined/google/protobuf/struct.ts @@ -0,0 +1,548 @@ +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; + +export const protobufPackage = "google.protobuf"; + +/** + * `NullValue` is a singleton enumeration to represent the null value for the + * `Value` type union. + * + * The JSON representation for `NullValue` is JSON `null`. + */ +export enum NullValue { + /** NULL_VALUE - Null value. */ + NULL_VALUE = 0, + UNRECOGNIZED = -1, +} + +export function nullValueFromJSON(object: any): NullValue { + switch (object) { + case 0: + case "NULL_VALUE": + return NullValue.NULL_VALUE; + case -1: + case "UNRECOGNIZED": + default: + return NullValue.UNRECOGNIZED; + } +} + +export function nullValueToJSON(object: NullValue): string { + switch (object) { + case NullValue.NULL_VALUE: + return "NULL_VALUE"; + case NullValue.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +/** + * `Struct` represents a structured data value, consisting of fields + * which map to dynamically typed values. In some languages, `Struct` + * might be supported by a native representation. For example, in + * scripting languages like JS a struct is represented as an + * object. The details of that representation are described together + * with the proto support for the language. + * + * The JSON representation for `Struct` is JSON object. + */ +export interface Struct { + /** Unordered map of dynamically typed values. */ + fields?: { [key: string]: any | undefined } | undefined; +} + +export interface Struct_FieldsEntry { + key: string; + value?: any | undefined; +} + +/** + * `Value` represents a dynamically typed value which can be either + * null, a number, a string, a boolean, a recursive struct value, or a + * list of values. A producer of value is expected to set one of these + * variants. Absence of any variant indicates an error. + * + * The JSON representation for `Value` is JSON value. + */ +export interface Value { + /** Represents a null value. */ + nullValue?: + | NullValue + | undefined; + /** Represents a double value. */ + numberValue?: + | number + | undefined; + /** Represents a string value. */ + stringValue?: + | string + | undefined; + /** Represents a boolean value. */ + boolValue?: + | boolean + | undefined; + /** Represents a structured value. */ + structValue?: + | { [key: string]: any } + | undefined; + /** Represents a repeated `Value`. */ + listValue?: Array | undefined; +} + +/** + * `ListValue` is a wrapper around a repeated field of values. + * + * The JSON representation for `ListValue` is JSON array. + */ +export interface ListValue { + /** Repeated field of dynamically typed values. */ + values?: any[] | undefined; +} + +function createBaseStruct(): Struct { + return {}; +} + +export const Struct = { + encode(message: Struct, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + Object.entries(message.fields || {}).forEach(([key, value]) => { + if (value !== undefined) { + Struct_FieldsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim(); + } + }); + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Struct { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStruct(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + const entry1 = Struct_FieldsEntry.decode(reader, reader.uint32()); + if (entry1.value !== undefined) { + if (message.fields === undefined) { + message.fields = {}; + } + message.fields![entry1.key] = entry1.value; + } + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Struct { + return { + fields: isObject(object.fields) + ? Object.entries(object.fields).reduce<{ [key: string]: any | undefined }>((acc, [key, value]) => { + acc[key] = value as any | undefined; + return acc; + }, {}) + : undefined, + }; + }, + + toJSON(message: Struct): unknown { + const obj: any = {}; + if (message.fields) { + const entries = Object.entries(message.fields); + if (entries.length > 0) { + obj.fields = {}; + entries.forEach(([k, v]) => { + obj.fields[k] = v; + }); + } + } + return obj; + }, + + create, I>>(base?: I): Struct { + return Struct.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Struct { + const message = createBaseStruct(); + message.fields = (object.fields === undefined || object.fields === null) + ? undefined + : Object.entries(object.fields ?? {}).reduce<{ [key: string]: any | undefined }>((acc, [key, value]) => { + if (value !== undefined) { + acc[key] = value; + } + return acc; + }, {}); + return message; + }, + + wrap(object: { [key: string]: any } | undefined): Struct { + const struct = createBaseStruct(); + struct.fields ??= {}; + if (object !== undefined) { + for (const key of Object.keys(object)) { + struct.fields[key] = object[key]; + } + } + return struct; + }, + + unwrap(message: Struct): { [key: string]: any } { + const object: { [key: string]: any } = {}; + if (message.fields) { + for (const key of Object.keys(message.fields)) { + object[key] = message.fields[key]; + } + } + return object; + }, +}; + +function createBaseStruct_FieldsEntry(): Struct_FieldsEntry { + return { key: "" }; +} + +export const Struct_FieldsEntry = { + encode(message: Struct_FieldsEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== undefined) { + Value.encode(Value.wrap(message.value), writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Struct_FieldsEntry { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStruct_FieldsEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.value = Value.unwrap(Value.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Struct_FieldsEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object?.value) ? object.value : undefined, + }; + }, + + toJSON(message: Struct_FieldsEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== undefined) { + obj.value = message.value; + } + return obj; + }, + + create, I>>(base?: I): Struct_FieldsEntry { + return Struct_FieldsEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Struct_FieldsEntry { + const message = createBaseStruct_FieldsEntry(); + message.key = object.key ?? ""; + message.value = object.value ?? undefined; + return message; + }, +}; + +function createBaseValue(): Value { + return {}; +} + +export const Value = { + encode(message: Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.nullValue !== undefined) { + writer.uint32(8).int32(message.nullValue); + } + if (message.numberValue !== undefined) { + writer.uint32(17).double(message.numberValue); + } + if (message.stringValue !== undefined) { + writer.uint32(26).string(message.stringValue); + } + if (message.boolValue !== undefined) { + writer.uint32(32).bool(message.boolValue); + } + if (message.structValue !== undefined) { + Struct.encode(Struct.wrap(message.structValue), writer.uint32(42).fork()).ldelim(); + } + if (message.listValue !== undefined) { + ListValue.encode(ListValue.wrap(message.listValue), writer.uint32(50).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Value { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.nullValue = reader.int32() as any; + continue; + case 2: + if (tag !== 17) { + break; + } + + message.numberValue = reader.double(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.stringValue = reader.string(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.boolValue = reader.bool(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.structValue = Struct.unwrap(Struct.decode(reader, reader.uint32())); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.listValue = ListValue.unwrap(ListValue.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Value { + return { + nullValue: isSet(object.nullValue) ? nullValueFromJSON(object.nullValue) : undefined, + numberValue: isSet(object.numberValue) ? globalThis.Number(object.numberValue) : undefined, + stringValue: isSet(object.stringValue) ? globalThis.String(object.stringValue) : undefined, + boolValue: isSet(object.boolValue) ? globalThis.Boolean(object.boolValue) : undefined, + structValue: isObject(object.structValue) ? object.structValue : undefined, + listValue: globalThis.Array.isArray(object.listValue) ? [...object.listValue] : undefined, + }; + }, + + toJSON(message: Value): unknown { + const obj: any = {}; + if (message.nullValue !== undefined) { + obj.nullValue = nullValueToJSON(message.nullValue); + } + if (message.numberValue !== undefined) { + obj.numberValue = message.numberValue; + } + if (message.stringValue !== undefined) { + obj.stringValue = message.stringValue; + } + if (message.boolValue !== undefined) { + obj.boolValue = message.boolValue; + } + if (message.structValue !== undefined) { + obj.structValue = message.structValue; + } + if (message.listValue !== undefined) { + obj.listValue = message.listValue; + } + return obj; + }, + + create, I>>(base?: I): Value { + return Value.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Value { + const message = createBaseValue(); + message.nullValue = object.nullValue ?? undefined; + message.numberValue = object.numberValue ?? undefined; + message.stringValue = object.stringValue ?? undefined; + message.boolValue = object.boolValue ?? undefined; + message.structValue = object.structValue ?? undefined; + message.listValue = object.listValue ?? undefined; + return message; + }, + + wrap(value: any): Value { + const result = createBaseValue(); + if (value === null) { + result.nullValue = NullValue.NULL_VALUE; + } else if (typeof value === "boolean") { + result.boolValue = value; + } else if (typeof value === "number") { + result.numberValue = value; + } else if (typeof value === "string") { + result.stringValue = value; + } else if (globalThis.Array.isArray(value)) { + result.listValue = value; + } else if (typeof value === "object") { + result.structValue = value; + } else if (typeof value !== "undefined") { + throw new globalThis.Error("Unsupported any value type: " + typeof value); + } + return result; + }, + + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.stringValue !== undefined) { + return message.stringValue; + } else if (message?.numberValue !== undefined) { + return message.numberValue; + } else if (message?.boolValue !== undefined) { + return message.boolValue; + } else if (message?.structValue !== undefined) { + return message.structValue as any; + } else if (message?.listValue !== undefined) { + return message.listValue; + } else if (message?.nullValue !== undefined) { + return null; + } + return undefined; + }, +}; + +function createBaseListValue(): ListValue { + return {}; +} + +export const ListValue = { + encode(message: ListValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.values !== undefined && message.values.length !== 0) { + for (const v of message.values) { + Value.encode(Value.wrap(v!), writer.uint32(10).fork()).ldelim(); + } + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ListValue { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseListValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + if (message.values === undefined) { + message.values = []; + } + message.values!.push(Value.unwrap(Value.decode(reader, reader.uint32()))); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ListValue { + return { values: globalThis.Array.isArray(object?.values) ? [...object.values] : [] }; + }, + + toJSON(message: ListValue): unknown { + const obj: any = {}; + if (message.values?.length) { + obj.values = message.values; + } + return obj; + }, + + create, I>>(base?: I): ListValue { + return ListValue.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ListValue { + const message = createBaseListValue(); + message.values = object.values?.map((e) => e) || undefined; + return message; + }, + + wrap(array: Array | undefined): ListValue { + const result = createBaseListValue(); + result.values = array ?? []; + return result; + }, + + unwrap(message: ListValue): Array { + if (message?.hasOwnProperty("values") && globalThis.Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/use-optionals-no-undefined/test.bin b/integration/use-optionals-no-undefined/test.bin index 11c02597f2d0e69c9ad97bc5c7c0eff732c3b4ee..b55039e572d121666b0f814dc45c920fed43eb90 100644 GIT binary patch literal 7382 zcmbtZOH&)!6>fFwc_s8{KnQ{HHO9}vVnSw86Hii8l4zi9Sz=lejx*&LAvK_kP*dHK zJ-3BQmINMi&Q1cWRXQysmd}xD7)l4x4W+eeq<3Aa3AM<_q^^or!S06Y;RJz zaGi-$PA0Ydd?~W7eD|MqwtDI~rz3kyY#mQew!Dcu?FXcK&foPJvAh232#dD`h5iML zpA7BMz$-<#c0av+yK;@V;uBVO1(h+LxR+MIQvQmiXO{Im)4s7U0Tj8PvS_Gpve@h1 zXlj>KtfjY$?|>iDuH_RwRzHud|3zgnu?Wf7csd%r@&O1myIp<RmGBsdZIt3J_9#*Q5)6e9|fBppR-KEaPz2K2CY zfD!{qdVA>|Nqsv8@F4fOq2*}kg}iaW`>vJ^kO+D@n&`!(7H83KhU3Bd7c3ui zj8A@ZgT?jen)0&<`&WiVGP<^==-dCuaKX@bM!YsXIk8=jKjJ>rJ&zB1lO7+AC$`%^ zgFrSvaoqFXgz>s_ap?|E&nA5PlTSVm7`Qp^S2(YZMqKQ9+_gR1eQggajGs*=7vAHo zt%3d89yu2{C^QD_;%f;Z9zk*vVv8|u*#kJp9Uf07Lubr;;{l&~HpJncGj;p6SUDb! zd+w!>%iH8{hLbbyxZSgUMNMf?uTF zXym*>9P_?29t?@eBPQV3lgAK=ejZ*E%;U~UC{f=T*!+CzO`!Rt2T2LFckH~jff!hf z@v$=*_U%n<4n0mmNC>khLes0_z!3dWZ+LFIu;lxNz+p4C2!()-gDFfUIQ{9lJ)Q^?lS{T>e+N4*N2J*9us8DFnp!vrn~cx;Rw~uB;XrT_@VIwwlVSW| z^kZub>~K%meK_$*BP3G_69>_I-n-<-Hbn}I=x}>Hz$$=T65`yMAOeIn=~VYPX+xr5ba$1-c(PRnXDzFuvEbzSUKn=iQW zlLO0Ww|UFr=Cgx_2{gc4)n>;u+MC>L)*Ia&v$?m)fxw%s4&x2;nb`qdr?n}LeV327 zcKI{Is_z4)S~DAF=Y=?M*X%Uuu-%r$c$FVit&UmmHmVjs=voJ@w!ulW9kX3;RLy6` zP6g-VOl}++%?@wxR~wD1iZb4M-ZU(depXFhGazNP)-dQO9Q^{ImQnAJLT^WP7!LwA zHW_ap73+Z2W~n;pZlx(6Mz z)qDUiKZj+YdKJ7o!osa4X-lzUw5%5dOLh<**yPXm4Xne_P2XBoGDsW2QSZ!>;XuR+ z9Q{^L-Zb_a=AO~48zfJQV4j<8;{hCILbg4^Br`v+;!p$%4yK5NTyPPMXF?Z7hCjKv z%d0zw0NDwt5dm#82p2H5yY>CR<_P~Ji>SKBl@g7Ty0)&Mj50NO#~=EOx0Rb>@usfP z&|ln8cEw^^*J$W3-cdHiVno+y=r7(476DH~fAOBOE-0C<(a>MKuiO%g8@firziLd4 zDtdHV`7BcYqsEW?f_sDneL>Y}j6BfeC^KjzrY@3!D0!mpZ&G6Mke;a^AQAcP9pi7# zhW#_-nG4DyTp2EXXPekoRp7N#_7miEi z==c(XjFCoBETnKEt5*0fQjh&@@BCtfvN0Sp?)BZ_g%C`_qmq1M^CxYo4o5zdKscZW zO~L9M|J+7L%R7|3sY)R~LpxCfC3%p|kq8GioplWUiF0aGc7_G0%u*gkDuoRK9jFM4 z)lejn8bS4>jo`lqO|+poNKio&h{o`vMHK|Vb~4L;$f8k2RdnrBm6tbp7c~Hh8}g6` zAovG8z*vUGh&4{U3*ii~pDwP1GYzw~o9v$|!X%eskqeJTau0_cQP6`5| z-$MnqxnIFh)brXsYDi#~{?zrz=Y9Bgs>ft=5XMwB zC|)R$&|IvN@$7F=r6`8T88+I9v8bHzkcUe9x`!f+IIr;PF#;al0C;=j%Wwk$HJ}#B zrug8t>pJK%kf=W9xxhT3eilBuLiJ-rlwMgUqbuLPjTdB(e-zLT$kssJ@4s0vU+f~o*D0Qyi! zipgHIRK9Ul(jZ>0p=vaCV=(|fg~$FBVQLIf z(^d{6<=;m5k^H7QqEN>*!X+Bn6MPZaxLdaCrz%9g%rGJey$zT!FR@S_k*N7}9CU9V zkMyg@i!^ma*hhZ2$0+XFu`DaIXiTK3u39M1ablbG7wv(vt7gbUG(-G!b9wel=o?k_ z=-2o%E5GJP^7}JT_l?`Tz&8s%L!2ot4`qzNx}qM*+vGu>k85%jS!mL8-QK0INR-0z z>tumFM#!ji3ty_M z&xWHx@QAWdxdLCQ)1Kv%;Nw5$;hV-T(OX7#yL64VO?w97&+^H1okd5^__Rdt8k!}> z`T~ogub7;b=p`d*`4gS9v|m$&tR;G>NLjN>ghgFQDS@qwCC2(9i^K7+pidglOED{k znK{tE$5OQHtCbNCPw!VN2{NIe{+Z9>Yc~g$k7-A2LRWp6(fE~6G7B!vN?_iA&N?1G zXv^0%hrOglv>no2zsGX4IjeFWBq=M4{ir|?CLxMVmLr5&tCT^P+K*XTY|PkY<~(7V6_922 z5^(O*iylk zRMX)MY{^U^oPjO55DJM&N)5ND!AQx|sKH2~OY=|Dh!W+?*9{4uNwz51m>LfVseNFH zkd4|0CfPukR+j_9n3(zPjG|I=kWPh7J2eOCrBZMjw8_ZR;$X{2A>&}nNFn23%gECb zx|UUM1g9k+ayF<@ikbjOHmFgU0VEsLD9iwoEw2V=;{!CO@&F_WkemcbLav+yNdhES zD2J2)$=$dafTVOSuWkfeDS+f9ND3hNbf|Xx*GM^3uDg%&t{Z>c`kok?ffh2Rfwjd>$1IU5|nFGjzRA&w#3sRjqfGkK!vbt83 zF3tj^C_%CSDN2wmK#J1ES%4I!i*vfRBwd^X$dUxf!8=P5BnOZs>EawfmZXdGx>k}d z&I6<*LGl18Nf+k;X_Ep#mL*65>MTo13IJJ_HYosPS=vO`wXy`! z0aBJAIzYMQ_cRjRW9kX0$kBK~oq-VZ`(5vE*I7sDCY){5)l3~Xz6?gimh#0!;r zC)g^2jjR5*qM{bT#+SpJR0JDe-=JhppCPxEdm@>OKL0lQd?Y--&)-n91Q#^+8)_F% literal 2755 zcmaKu?QR=I6oz+a_H*|8WY=E1Y2vs|Qz{gRA`t2yq|jC_phQ}N_!*fxq7fmf5*Mjl z0oTGkaSvPq@67HT+@kj1oH^%x=6uYKtpT?*I07^7`tR)GX>HYy`--$gXCk?X_={&CQ7f*Op!TMvI}8Jq_KVe@u1jC4So``8jNtx2 z2NrG@A<*z?$wz71pOKGKGTFj~_Ht=&-?)%T)>R4&jB?C5T2|Ey>C4VfSyT6l(F zTB0L9RfQ&Oc4dmVc;Rzn1(?d4K`5V!3P+Lt^& zeDBVlYU%-8+ow*Q)wo2$f3ydj8gYQgNr3`i}p;ynJ(RzNxtDjVIzO z_b683nXd6-b(AV)mS#w7nbsdADmFfSvDjrgcSNc*u8l5nxbDR~RKA(#qfb<9hNHho zN>upc@ps23KOL721xlP8mx4kiPo6(7<+p#QDj?5s9=Iw}Zc5T|NUsD**BL3Lgy)ba zox3j9(sNvrGHO(!JkJphNlz3iRlYWSZH3lCpcg4?NmyAv$l&q?4K6p(mX%?8(vLFh zgUm0hL57q9F&bO~vOpx(f-FcYYe5#&Hc1$vur7%tLq1JH7DnkHlRB~}htoWg5z9Qt zA`uLM9%NByWgcV^%Mcig$q?upE3y`3aa>yqvUuDMr=c3frv;EHPBMT@agqUKicbqk zrsD2E43TmwY|BGH(xkLLK+8c3?V>Th7cevkC*^ydBkX|Y#Fjea>66t0%XFFEkGtb;w?ZXJmM`t zCOqPr%CSq&h4V|T#zMt^HwZ|^~^6m+52-NTYUKkC5 oFy?(-P-)p*_(P!3z9=?76fSxD`iDgSP-xtKl>Ho-f32VY3-{H70ssI2 diff --git a/integration/use-optionals-no-undefined/test.proto b/integration/use-optionals-no-undefined/test.proto index d752ce261..5a888b9c5 100644 --- a/integration/use-optionals-no-undefined/test.proto +++ b/integration/use-optionals-no-undefined/test.proto @@ -1,6 +1,8 @@ syntax = "proto3"; package optionalstest; +import "google/protobuf/struct.proto"; + message OptionalsTest { int32 id = 1; Child child = 2; @@ -27,6 +29,8 @@ message OptionalsTest { optional bytes opt_data = 27; map translations = 30; + + google.protobuf.Struct struct = 32; } enum StateEnum { diff --git a/integration/use-optionals-no-undefined/test.ts b/integration/use-optionals-no-undefined/test.ts index 0cb49c2b9..a8ac8b164 100644 --- a/integration/use-optionals-no-undefined/test.ts +++ b/integration/use-optionals-no-undefined/test.ts @@ -1,5 +1,6 @@ /* eslint-disable */ import * as _m0 from "protobufjs/minimal"; +import { Struct } from "./google/protobuf/struct"; import Long = require("long"); export const protobufPackage = "optionalstest"; @@ -66,6 +67,7 @@ export interface OptionalsTest { optDescription?: string | undefined; optData?: Uint8Array | undefined; translations?: { [key: string]: string } | undefined; + struct?: { [key: string]: any } | undefined; } export interface OptionalsTest_TranslationsEntry { @@ -170,6 +172,9 @@ export const OptionalsTest = { Object.entries(message.translations || {}).forEach(([key, value]) => { OptionalsTest_TranslationsEntry.encode({ key: key as any, value }, writer.uint32(242).fork()).ldelim(); }); + if (message.struct !== undefined) { + Struct.encode(Struct.wrap(message.struct), writer.uint32(258).fork()).ldelim(); + } return writer; }, @@ -413,6 +418,13 @@ export const OptionalsTest = { message.translations![entry30.key] = entry30.value; } continue; + case 32: + if (tag !== 258) { + break; + } + + message.struct = Struct.unwrap(Struct.decode(reader, reader.uint32())); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -463,6 +475,7 @@ export const OptionalsTest = { return acc; }, {}) : undefined, + struct: isObject(object.struct) ? object.struct : undefined, }; }, @@ -540,6 +553,9 @@ export const OptionalsTest = { }); } } + if (message.struct !== undefined) { + obj.struct = message.struct; + } return obj; }, @@ -579,6 +595,7 @@ export const OptionalsTest = { } return acc; }, {}); + message.struct = object.struct ?? undefined; return message; }, }; diff --git a/integration/use-readonly-types/google/protobuf/struct.ts b/integration/use-readonly-types/google/protobuf/struct.ts index 1c1c45bca..83ef5f717 100644 --- a/integration/use-readonly-types/google/protobuf/struct.ts +++ b/integration/use-readonly-types/google/protobuf/struct.ts @@ -170,10 +170,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -181,9 +182,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/integration/value/google/protobuf/struct.ts b/integration/value/google/protobuf/struct.ts index 5760ebc59..849206f64 100644 --- a/integration/value/google/protobuf/struct.ts +++ b/integration/value/google/protobuf/struct.ts @@ -184,10 +184,11 @@ export const Struct = { wrap(object: { [key: string]: any } | undefined): Struct { const struct = createBaseStruct(); + if (object !== undefined) { - Object.keys(object).forEach((key) => { + for (const key of Object.keys(object)) { struct.fields[key] = object[key]; - }); + } } return struct; }, @@ -195,9 +196,9 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach((key) => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }, diff --git a/src/generate-struct-wrappers.ts b/src/generate-struct-wrappers.ts index bcf65d4d9..b08e08459 100644 --- a/src/generate-struct-wrappers.ts +++ b/src/generate-struct-wrappers.ts @@ -31,15 +31,20 @@ export function generateWrapDeep(ctx: Context, fullProtoTypeName: string, fieldN const chunks: Code[] = []; if (isStructTypeName(fullProtoTypeName)) { let setStatement = "struct.fields[key] = Value.wrap(object[key]);"; + let defaultFields = "struct.fields ??= {};"; if (ctx.options.useMapType) { setStatement = "struct.fields.set(key, Value.wrap(object[key]));"; + defaultFields = "struct.fields ??= new Map();"; } + if (ctx.options.useOptionals !== "all") defaultFields = ""; + chunks.push(code`wrap(object: {[key: string]: any} | undefined): Struct { const struct = createBaseStruct(); + ${defaultFields} if (object !== undefined) { - Object.keys(object).forEach(key => { + for (const key of Object.keys(object)) { ${setStatement} - }); + } } return struct; }`); @@ -99,18 +104,20 @@ export function generateUnwrapDeep(ctx: Context, fullProtoTypeName: string, fiel if (ctx.options.useMapType) { chunks.push(code`unwrap(message: Struct): {[key: string]: any} { const object: { [key: string]: any } = {}; - [...message.fields.keys()].forEach((key) => { - object[key] = Value.unwrap(message.fields.get(key)); - }); + if (message.fields) { + for (const key of message.fields.keys()) { + object[key] = Value.unwrap(message.fields.get(key)); + } + } return object; }`); } else { chunks.push(code`unwrap(message: Struct): {[key: string]: any} { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach(key => { + for (const key of Object.keys(message.fields)) { object[key] = Value.unwrap(message.fields[key]); - }); + } } return object; }`); @@ -165,15 +172,20 @@ export function generateWrapShallow(ctx: Context, fullProtoTypeName: string, fie const chunks: Code[] = []; if (isStructTypeName(fullProtoTypeName)) { let setStatement = "struct.fields[key] = object[key];"; + let defaultFields = "struct.fields ??= {};"; if (ctx.options.useMapType) { setStatement = "struct.fields.set(key, object[key]);"; + defaultFields = "struct.fields ??= new Map();"; } + if (ctx.options.useOptionals !== "all") defaultFields = ""; + chunks.push(code`wrap(object: {[key: string]: any} | undefined): Struct { const struct = createBaseStruct(); + ${defaultFields} if (object !== undefined) { - Object.keys(object).forEach(key => { + for (const key of Object.keys(object)) { ${setStatement} - }); + } } return struct; }`); @@ -254,18 +266,20 @@ export function generateUnwrapShallow(ctx: Context, fullProtoTypeName: string, f if (ctx.options.useMapType) { chunks.push(code`unwrap(message: Struct): {[key: string]: any} { const object: { [key: string]: any } = {}; - [...message.fields.keys()].forEach((key) => { - object[key] = message.fields.get(key); - }); + if (message.fields) { + for (const key of message.fields.keys()) { + object[key] = message.fields.get(key); + } + } return object; }`); } else { chunks.push(code`unwrap(message: Struct): {[key: string]: any} { const object: { [key: string]: any } = {}; if (message.fields) { - Object.keys(message.fields).forEach(key => { + for (const key of Object.keys(message.fields)) { object[key] = message.fields[key]; - }); + } } return object; }`); From b9b0ff7b2a6eb4e04ce6dab5f97e2d3d40d2b385 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 28 Feb 2024 17:25:41 +0000 Subject: [PATCH 2/4] chore(release): 1.167.9 [skip ci] ## [1.167.9](https://github.com/stephenh/ts-proto/compare/v1.167.8...v1.167.9) (2024-02-28) ### Bug Fixes * typescript errors for struct with optional=all ([#1008](https://github.com/stephenh/ts-proto/issues/1008)) ([e838e38](https://github.com/stephenh/ts-proto/commit/e838e3801e0ef5e8b5a14ead7d7dfc0ad3532cf1)), closes [#578](https://github.com/stephenh/ts-proto/issues/578) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37b7c2708..08a5c53a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.167.9](https://github.com/stephenh/ts-proto/compare/v1.167.8...v1.167.9) (2024-02-28) + + +### Bug Fixes + +* typescript errors for struct with optional=all ([#1008](https://github.com/stephenh/ts-proto/issues/1008)) ([e838e38](https://github.com/stephenh/ts-proto/commit/e838e3801e0ef5e8b5a14ead7d7dfc0ad3532cf1)), closes [#578](https://github.com/stephenh/ts-proto/issues/578) + ## [1.167.8](https://github.com/stephenh/ts-proto/compare/v1.167.7...v1.167.8) (2024-02-18) diff --git a/package.json b/package.json index 0323229d9..847ea8dd9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ts-proto", - "version": "1.167.8", + "version": "1.167.9", "description": "", "main": "build/plugin.js", "repository": "github:stephenh/ts-proto", From f285557a4f77d4b75327de2c13cf0917b0361f14 Mon Sep 17 00:00:00 2001 From: Luke Alvoeiro Date: Fri, 8 Mar 2024 07:13:41 -0800 Subject: [PATCH 3/4] feat: allow `$type` to be optional (#1013) ### Description Add another possible value to the `outputTypeAnnotations` option: `optional`. This makes type definitions on interfaces optional, which may be useful if you want to use the `$type` field for runtime type checking on responses from a server but don't want to have to set the $type on each request message you create. ### Testing Ensured this causes no regressions for existing integration tests. Also added new integration tests to prevent future regressions. --- README.markdown | 2 +- .../optional-type-definitions/parameters.txt | 1 + .../optional-type-definitions/simple.bin | Bin 0 -> 1147 bytes .../optional-type-definitions/simple.proto | 23 ++ .../optional-type-definitions/simple.ts | 239 ++++++++++++++++++ src/main.ts | 2 +- src/options.ts | 2 +- 7 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 integration/optional-type-definitions/parameters.txt create mode 100644 integration/optional-type-definitions/simple.bin create mode 100644 integration/optional-type-definitions/simple.proto create mode 100644 integration/optional-type-definitions/simple.ts diff --git a/README.markdown b/README.markdown index a6834d21a..21a53dd14 100644 --- a/README.markdown +++ b/README.markdown @@ -432,7 +432,7 @@ Generated code will be placed in the Gradle build directory. - With `--ts_proto_opt=outputSchema=true`, meta typings will be generated that can later be used in other code generators. -- With `--ts_proto_opt=outputTypeAnnotations=true`, each message will be given a `$type` field containing its fully-qualified name. You can use `--ts_proto_opt=outputTypeAnnotations=static-only` to omit it from the `interface` declaration. +- With `--ts_proto_opt=outputTypeAnnotations=true`, each message will be given a `$type` field containing its fully-qualified name. You can use `--ts_proto_opt=outputTypeAnnotations=static-only` to omit it from the `interface` declaration, or `--ts_proto_opt=outputTypeAnnotations=optional` to make it an optional property on the `interface` definition. The latter option may be useful if you want to use the `$type` field for runtime type checking on responses from a server. - With `--ts_proto_opt=outputTypeRegistry=true`, the type registry will be generated that can be used to resolve message types by fully-qualified name. Also, each message will be given a `$type` field containing its fully-qualified name. diff --git a/integration/optional-type-definitions/parameters.txt b/integration/optional-type-definitions/parameters.txt new file mode 100644 index 000000000..c2e04e81c --- /dev/null +++ b/integration/optional-type-definitions/parameters.txt @@ -0,0 +1 @@ +outputTypeAnnotations=optional diff --git a/integration/optional-type-definitions/simple.bin b/integration/optional-type-definitions/simple.bin new file mode 100644 index 0000000000000000000000000000000000000000..02c48b052f268a42c055c0da51b4509fe0155e61 GIT binary patch literal 1147 zcmY*ZyK>V&6xFWQLcUQfX&nc}FvG@#kO@hO@Mx&QtDr~`sAw{>wT?B&T937vB=A)< zd=D*u!CgtKA)U^BoO92;k|3z^<*JY;tF_WstB1>G1Z=7zPw94;Iur6!zd>25= zN?*=%iPfwl@0;i+6Xd5btV>FzFEhDX%UP;rmh{?M=OQ-gbEQ9Y$;l(=)l!`QKXxE_L|$n>fp|yu1>rn5pGbmRe{f^g@d2uGHzRxXFt`OeH2|9iHdw zN<&wimvvNK>(#XumAYQfq?qTpZ2%t#u|+;2{tPz3Gkv~E4)>JR%ad0Tk(0{EU+CG8`1Ny)sQ|WJ5gd~GO zXb~z5qNl9_F$#lt0>`xnJV;3QC*uBQ>m#Q{r9`UD!roe6)HC%~m7;3sTVE83!_F5) z)v)tLjcSO#HfmJ_5j|?>bJ0oE?aD>Ys6VthQ@Q=nBIJyc7p)W5a7H_aEhpn{Oy9JX z3_0VDlObo^e{6Hu7C*6jWXKuszHT|0;fyD*@Y)o+*p~S^%;gDEY@t;%KQWgdQC{;Q ZK&?@$g$N5r?X3|Z-6)EuUj46m`46k3%ZUI0 literal 0 HcmV?d00001 diff --git a/integration/optional-type-definitions/simple.proto b/integration/optional-type-definitions/simple.proto new file mode 100644 index 000000000..3d8dbdf88 --- /dev/null +++ b/integration/optional-type-definitions/simple.proto @@ -0,0 +1,23 @@ +// Adding a comment to the syntax will become the first +// comment in the output source file. +syntax = "proto3"; + +package simple; + +// This comment is separated by a blank non-comment line, and will detach from +// the following comment on the message Simple. + +/** Example comment on the Simple message */ +message Simple { + // Name field + string name = 1 [deprecated = true]; + /** Age field */ + int32 age = 2 [deprecated = true]; + Child child = 3 [deprecated = true]; // This comment will also attach; + string test_field = 4 [deprecated = true]; + string test_not_deprecated = 5 [deprecated = false]; +} + +message Child { + string name = 1; +} diff --git a/integration/optional-type-definitions/simple.ts b/integration/optional-type-definitions/simple.ts new file mode 100644 index 000000000..9e5c489be --- /dev/null +++ b/integration/optional-type-definitions/simple.ts @@ -0,0 +1,239 @@ +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; + +export const protobufPackage = "simple"; + +/** + * Adding a comment to the syntax will become the first + * comment in the output source file. + */ + +/** Example comment on the Simple message */ +export interface Simple { + $type?: "simple.Simple"; + /** + * Name field + * + * @deprecated + */ + name: string; + /** + * Age field + * + * @deprecated + */ + age: number; + /** + * This comment will also attach; + * + * @deprecated + */ + child: + | Child + | undefined; + /** @deprecated */ + testField: string; + testNotDeprecated: string; +} + +export interface Child { + $type?: "simple.Child"; + name: string; +} + +function createBaseSimple(): Simple { + return { $type: "simple.Simple", name: "", age: 0, child: undefined, testField: "", testNotDeprecated: "" }; +} + +export const Simple = { + $type: "simple.Simple" as const, + + encode(message: Simple, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + if (message.age !== 0) { + writer.uint32(16).int32(message.age); + } + if (message.child !== undefined) { + Child.encode(message.child, writer.uint32(26).fork()).ldelim(); + } + if (message.testField !== "") { + writer.uint32(34).string(message.testField); + } + if (message.testNotDeprecated !== "") { + writer.uint32(42).string(message.testNotDeprecated); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Simple { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSimple(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.age = reader.int32(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.child = Child.decode(reader, reader.uint32()); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.testField = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.testNotDeprecated = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Simple { + return { + $type: Simple.$type, + name: isSet(object.name) ? globalThis.String(object.name) : "", + age: isSet(object.age) ? globalThis.Number(object.age) : 0, + child: isSet(object.child) ? Child.fromJSON(object.child) : undefined, + testField: isSet(object.testField) ? globalThis.String(object.testField) : "", + testNotDeprecated: isSet(object.testNotDeprecated) ? globalThis.String(object.testNotDeprecated) : "", + }; + }, + + toJSON(message: Simple): unknown { + const obj: any = {}; + if (message.name !== "") { + obj.name = message.name; + } + if (message.age !== 0) { + obj.age = Math.round(message.age); + } + if (message.child !== undefined) { + obj.child = Child.toJSON(message.child); + } + if (message.testField !== "") { + obj.testField = message.testField; + } + if (message.testNotDeprecated !== "") { + obj.testNotDeprecated = message.testNotDeprecated; + } + return obj; + }, + + create, I>>(base?: I): Simple { + return Simple.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Simple { + const message = createBaseSimple(); + message.name = object.name ?? ""; + message.age = object.age ?? 0; + message.child = (object.child !== undefined && object.child !== null) ? Child.fromPartial(object.child) : undefined; + message.testField = object.testField ?? ""; + message.testNotDeprecated = object.testNotDeprecated ?? ""; + return message; + }, +}; + +function createBaseChild(): Child { + return { $type: "simple.Child", name: "" }; +} + +export const Child = { + $type: "simple.Child" as const, + + encode(message: Child, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Child { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseChild(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Child { + return { $type: Child.$type, name: isSet(object.name) ? globalThis.String(object.name) : "" }; + }, + + toJSON(message: Child): unknown { + const obj: any = {}; + if (message.name !== "") { + obj.name = message.name; + } + return obj; + }, + + create, I>>(base?: I): Child { + return Child.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Child { + const message = createBaseChild(); + message.name = object.name ?? ""; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in Exclude]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude | "$type">]: never }; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/main.ts b/src/main.ts index cd333e1f0..85cbd16f9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -956,7 +956,7 @@ function generateInterfaceDeclaration( chunks.push(code`export interface ${def(fullName)} {`); if (addTypeToMessages(options)) { - chunks.push(code`$type: '${fullTypeName}',`); + chunks.push(code`$type${options.outputTypeAnnotations === "optional" ? "?" : ""}: '${fullTypeName}',`); } // When oneof=unions, we generate a single property with an ADT per `oneof` clause. diff --git a/src/options.ts b/src/options.ts index 11d849d3c..1bf426bbd 100644 --- a/src/options.ts +++ b/src/options.ts @@ -55,7 +55,7 @@ export type Options = { outputEncodeMethods: true | false | "encode-only" | "decode-only" | "encode-no-creation"; outputJsonMethods: true | false | "to-only" | "from-only"; outputPartialMethods: boolean; - outputTypeAnnotations: boolean | "static-only"; + outputTypeAnnotations: boolean | "static-only" | "optional"; outputTypeRegistry: boolean; stringEnums: boolean; constEnums: boolean; From 51c3d0c5c193e849c728460bd8fece8af20a895c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 8 Mar 2024 15:17:41 +0000 Subject: [PATCH 4/4] chore(release): 1.168.0 [skip ci] # [1.168.0](https://github.com/stephenh/ts-proto/compare/v1.167.9...v1.168.0) (2024-03-08) ### Features * allow `$type` to be optional ([#1013](https://github.com/stephenh/ts-proto/issues/1013)) ([f285557](https://github.com/stephenh/ts-proto/commit/f285557a4f77d4b75327de2c13cf0917b0361f14)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08a5c53a7..014e1c750 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [1.168.0](https://github.com/stephenh/ts-proto/compare/v1.167.9...v1.168.0) (2024-03-08) + + +### Features + +* allow `$type` to be optional ([#1013](https://github.com/stephenh/ts-proto/issues/1013)) ([f285557](https://github.com/stephenh/ts-proto/commit/f285557a4f77d4b75327de2c13cf0917b0361f14)) + ## [1.167.9](https://github.com/stephenh/ts-proto/compare/v1.167.8...v1.167.9) (2024-02-28) diff --git a/package.json b/package.json index 847ea8dd9..a93404a2f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ts-proto", - "version": "1.167.9", + "version": "1.168.0", "description": "", "main": "build/plugin.js", "repository": "github:stephenh/ts-proto",