diff --git a/integration/simple-proto2/simple.proto b/integration/simple-proto2/simple.proto index 4980a813c..59ac6530b 100644 --- a/integration/simple-proto2/simple.proto +++ b/integration/simple-proto2/simple.proto @@ -8,4 +8,5 @@ enum EnumWithoutZero { message Issue56 { required EnumWithoutZero test = 1; + optional int32 foo = 2; } diff --git a/integration/simple-proto2/simple.ts b/integration/simple-proto2/simple.ts index 30321dd8c..52c9f7f38 100644 --- a/integration/simple-proto2/simple.ts +++ b/integration/simple-proto2/simple.ts @@ -1,12 +1,15 @@ +/* eslint-disable */ import { Writer, Reader } from 'protobufjs/minimal'; export interface Issue56 { test: EnumWithoutZero; + foo?: number; } const baseIssue56: object = { test: 1, + foo: 0, }; export const protobufPackage = 'simple' @@ -46,6 +49,9 @@ export function enumWithoutZeroToJSON(object: EnumWithoutZero): string { export const Issue56 = { encode(message: Issue56, writer: Writer = Writer.create()): Writer { writer.uint32(8).int32(message.test); + if (message.foo !== undefined) { + writer.uint32(16).int32(message.foo); + } return writer; }, decode(input: Uint8Array | Reader, length?: number): Issue56 { @@ -58,6 +64,9 @@ export const Issue56 = { case 1: message.test = reader.int32() as any; break; + case 2: + message.foo = reader.int32(); + break; default: reader.skipType(tag & 7); break; @@ -72,6 +81,9 @@ export const Issue56 = { } else { message.test = 1; } + if (object.foo !== undefined && object.foo !== null) { + message.foo = Number(object.foo); + } return message; }, fromPartial(object: DeepPartial): Issue56 { @@ -81,11 +93,15 @@ export const Issue56 = { } else { message.test = 1; } + if (object.foo !== undefined && object.foo !== null) { + message.foo = object.foo; + } return message; }, toJSON(message: Issue56): unknown { const obj: any = {}; message.test !== undefined && (obj.test = enumWithoutZeroToJSON(message.test)); + message.foo !== undefined && (obj.foo = message.foo); return obj; }, }; diff --git a/src/main.ts b/src/main.ts index 2d7527f25..9a87e4249 100644 --- a/src/main.ts +++ b/src/main.ts @@ -482,7 +482,11 @@ function generateEnumToJson(fullName: string, enumDesc: EnumDescriptorProto): Fu // When useOptionals=true, non-scalar fields are translated into optional properties. function isOptionalProperty(field: FieldDescriptorProto, options: Options): boolean { - return (options.useOptionals && isMessage(field) && !isRepeated(field)) || field.proto3Optional; + return ( + (options.useOptionals && isMessage(field) && !isRepeated(field)) || + field.proto3Optional || + field.label === FieldDescriptorProto.Label.LABEL_OPTIONAL + ); } // Create the interface with properties @@ -847,6 +851,11 @@ function generateEncode( ) .addStatement('%L', writeSnippet(`message.${fieldName}`)) .endControlFlow(); + } else if (field.label === FieldDescriptorProto.Label.LABEL_OPTIONAL) { + func = func + .beginControlFlow('if (message.%L !== undefined)', fieldName) + .addStatement('%L', writeSnippet(`message.${fieldName}`)) + .endControlFlow(); } else { func = func.addStatement('%L', writeSnippet(`message.${fieldName}`)); } @@ -979,7 +988,9 @@ function generateFromJson( if ( !isRepeated(field) && field.type !== FieldDescriptorProto.Type.TYPE_BYTES && - options.oneof !== OneofOption.UNIONS + options.oneof !== OneofOption.UNIONS && + !field.proto3Optional && + field.label !== FieldDescriptorProto.Label.LABEL_OPTIONAL ) { func = func.nextControlFlow('else'); func = func.addStatement( @@ -1208,7 +1219,12 @@ function generateFromPartial( } } - if (!isRepeated(field) && options.oneof !== OneofOption.UNIONS) { + if ( + !isRepeated(field) && + options.oneof !== OneofOption.UNIONS && + !field.proto3Optional && + field.label !== FieldDescriptorProto.Label.LABEL_OPTIONAL + ) { func = func.nextControlFlow('else'); func = func.addStatement( `message.%L = %L`, diff --git a/src/types.ts b/src/types.ts index 619476524..36805479f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -377,7 +377,7 @@ export function toTypeName( field: FieldDescriptorProto, options: Options ): TypeName { - let type = basicTypeName(typeMap, field, options, { keepValueType: false }); + let type = basicTypeName(typeMap, field, options, { keepValueType: true }); if (isRepeated(field)) { const mapType = detectMapType(typeMap, messageDesc, field, options); if (mapType) { @@ -404,7 +404,10 @@ export function toTypeName( // clause, spelling each option out inside a large type union. No need for // union with `undefined` here, either. if ( - (!isWithinOneOf(field) && isMessage(field) && !options.useOptionals) || + (!isWithinOneOf(field) && + isMessage(field) && + !options.useOptionals && + field.label !== FieldDescriptorProto.Label.LABEL_REQUIRED) || (isWithinOneOf(field) && options.oneof === OneofOption.PROPERTIES) || (isWithinOneOf(field) && field.proto3Optional) ) {