@@ -377,7 +377,7 @@ function syntaxForType(type) {
377
377
return syntax !== null ? syntax : "proto2" ;
378
378
}
379
379
380
- function isOptional ( field , syntax ) {
380
+ function isExplicitPresence ( field , syntax ) {
381
381
382
382
// In proto3, optional fields are explicit
383
383
if ( syntax === "proto3" )
@@ -390,6 +390,19 @@ function isOptional(field, syntax) {
390
390
throw new Error ( "Unknown proto syntax: [" + syntax + "]" ) ;
391
391
}
392
392
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
+
393
406
function isOptionalOneOf ( oneof , syntax ) {
394
407
395
408
if ( syntax === "proto2" )
@@ -419,13 +432,17 @@ function buildType(ref, type) {
419
432
var jsType = toJsType ( field , /* parentIsInterface = */ true ) ;
420
433
var nullable = false ;
421
434
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 ) {
426
439
jsType = jsType + "|null|undefined" ;
427
440
nullable = true ;
428
441
}
442
+ else if ( isImplicitPresence ( field , syntax ) || field . repeated || field . map ) {
443
+ jsType = jsType + "|undefined" ;
444
+ nullable = true ;
445
+ }
429
446
}
430
447
else {
431
448
// Without semantic nulls, everything is optional in proto3
@@ -465,7 +482,7 @@ function buildType(ref, type) {
465
482
// With semantic nulls, fields are nullable if they are explicitly optional or part of a one-of
466
483
// Maps, repeated values and fields with implicit defaults are never null after construction
467
484
// 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 )
469
486
jsType = jsType + "|null" ;
470
487
}
471
488
else {
@@ -484,10 +501,10 @@ function buildType(ref, type) {
484
501
push ( "" ) ;
485
502
firstField = false ;
486
503
}
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
488
505
// Otherwise use field.optional, which doesn't consider proto3, maps, repeated fields etc.
489
506
var nullDefault = config [ "null-semantics" ]
490
- ? isOptional ( field , syntax )
507
+ ? isExplicitPresence ( field , syntax )
491
508
: field . optional && config [ "null-defaults" ] ;
492
509
if ( field . repeated )
493
510
push ( escapeName ( type . name ) + ".prototype" + prop + " = $util.emptyArray;" ) ; // overwritten in constructor
0 commit comments