@@ -377,7 +377,7 @@ function syntaxForType(type) {
377377 return syntax !== null ? syntax : "proto2" ;
378378}
379379
380- function isOptional ( field , syntax ) {
380+ function isExplicitPresence ( field , syntax ) {
381381
382382 // In proto3, optional fields are explicit
383383 if ( syntax === "proto3" )
@@ -390,6 +390,19 @@ function isOptional(field, syntax) {
390390 throw new Error ( "Unknown proto syntax: [" + syntax + "]" ) ;
391391}
392392
393+ function isImplicitPresence ( field , syntax ) {
394+
395+ // In proto3, everything not marked optional has implicit presence (including maps and repeated fields)
396+ if ( syntax === "proto3" )
397+ return field . options == null || field . options [ "proto3_optional" ] !== true ;
398+
399+ // In proto2, nothing has implicit presence
400+ if ( syntax === "proto2" )
401+ return false ;
402+
403+ throw new Error ( "Unknown proto syntax: [" + syntax + "]" ) ;
404+ }
405+
393406function isOptionalOneOf ( oneof , syntax ) {
394407
395408 if ( syntax === "proto2" )
@@ -419,13 +432,17 @@ function buildType(ref, type) {
419432 var jsType = toJsType ( field , /* parentIsInterface = */ true ) ;
420433 var nullable = false ;
421434 if ( config [ "null-semantics" ] ) {
422- // With semantic nulls, decide which fields are required for the current protobuf version
423- // Fields with implicit defaults in proto3 are required for the purpose of constructing objects
424- // Optional fields can be undefined, i.e. they can be omitted for the source object altogether
425- if ( isOptional ( field , syntax ) || field . partOf || field . repeated || field . map ) {
435+ // With semantic nulls, only explicit optional fields and one-of members can be set to null
436+ // Implicit fields (proto3), maps and lists can be omitted, but if specified must be non-null
437+ // Implicit fields will take their default value when the message is constructed
438+ if ( isExplicitPresence ( field , syntax ) || field . partOf ) {
426439 jsType = jsType + "|null|undefined" ;
427440 nullable = true ;
428441 }
442+ else if ( isImplicitPresence ( field , syntax ) || field . repeated || field . map ) {
443+ jsType = jsType + "|undefined" ;
444+ nullable = true ;
445+ }
429446 }
430447 else {
431448 // Without semantic nulls, everything is optional in proto3
@@ -465,7 +482,7 @@ function buildType(ref, type) {
465482 // With semantic nulls, fields are nullable if they are explicitly optional or part of a one-of
466483 // Maps, repeated values and fields with implicit defaults are never null after construction
467484 // Members are never undefined, at a minimum they are initialized to null
468- if ( isOptional ( field , syntax ) || field . partOf )
485+ if ( isExplicitPresence ( field , syntax ) || field . partOf )
469486 jsType = jsType + "|null" ;
470487 }
471488 else {
@@ -484,10 +501,10 @@ function buildType(ref, type) {
484501 push ( "" ) ;
485502 firstField = false ;
486503 }
487- // Semantic nulls respect the optional semantics for the current protobuf version
504+ // With semantic nulls, only explict optional fields and one-of members are null by default
488505 // Otherwise use field.optional, which doesn't consider proto3, maps, repeated fields etc.
489506 var nullDefault = config [ "null-semantics" ]
490- ? isOptional ( field , syntax )
507+ ? isExplicitPresence ( field , syntax )
491508 : field . optional && config [ "null-defaults" ] ;
492509 if ( field . repeated )
493510 push ( escapeName ( type . name ) + ".prototype" + prop + " = $util.emptyArray;" ) ; // overwritten in constructor
0 commit comments