ts-proto 1.95.1 → 1.97.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ## [1.97.1](https://github.com/stephenh/ts-proto/compare/v1.97.0...v1.97.1) (2022-01-05)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * oneof=union breaks wrapper types [#458](https://github.com/stephenh/ts-proto/issues/458) ([#462](https://github.com/stephenh/ts-proto/issues/462)) ([dd16992](https://github.com/stephenh/ts-proto/commit/dd16992a409f24e88e3a142830cd0745f50dbd10))
7
+
8
+ # [1.97.0](https://github.com/stephenh/ts-proto/compare/v1.96.1...v1.97.0) (2021-12-30)
9
+
10
+
11
+ ### Features
12
+
13
+ * add an option to disable Exact types ([#456](https://github.com/stephenh/ts-proto/issues/456)) ([9c53d7e](https://github.com/stephenh/ts-proto/commit/9c53d7efb0252c7ea0af85a5d161ff94bcd69760))
14
+
15
+ ## [1.96.1](https://github.com/stephenh/ts-proto/compare/v1.96.0...v1.96.1) (2021-12-28)
16
+
17
+
18
+ ### Performance Improvements
19
+
20
+ * optimize object creation in `decode`, `fromJSON` and `fromPartial` ([#457](https://github.com/stephenh/ts-proto/issues/457)) ([70832d3](https://github.com/stephenh/ts-proto/commit/70832d33bae82ecb3c5f87845d14e992a13437e4))
21
+
22
+ # [1.96.0](https://github.com/stephenh/ts-proto/compare/v1.95.1...v1.96.0) (2021-12-24)
23
+
24
+
25
+ ### Features
26
+
27
+ * `enumsAsLiterals` option ([#450](https://github.com/stephenh/ts-proto/issues/450)) ([fcaade2](https://github.com/stephenh/ts-proto/commit/fcaade2855ae28ea3553a365556ccb92a9644d70))
28
+
1
29
  ## [1.95.1](https://github.com/stephenh/ts-proto/compare/v1.95.0...v1.95.1) (2021-12-23)
2
30
 
3
31
 
package/README.markdown CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  > `ts-proto` transforms your `.proto` files into strongly-typed, idiomatic TypeScript files!
7
7
 
8
- (Note, if you're a new user of ts-proto and using a modern TS setup with `esModuleInterop`, you need to also pass that as a `ts_proto_opt`.)
8
+ (Note, if you're a new user of ts-proto and using a modern TS setup with `esModuleInterop`, or want to use ts-proto in ESM / snowpack / vite, you need to also pass that as a `ts_proto_opt`.)
9
9
 
10
10
  ## Table of contents
11
11
 
@@ -348,6 +348,12 @@ Generated code will be placed in the Gradle build directory.
348
348
 
349
349
  - With `--ts_proto_opt=fileSuffix=<SUFFIX>`, ts-proto will emit generated files using the specified suffix. A `helloworld.proto` file with `fileSuffix=.pb` would be generated as `helloworld.pb.ts`. This is common behavior in other protoc plugins and provides a way to quickly glob all the generated files.
350
350
 
351
+ - With `--ts_proto_opt=enumsAsLiterals=true`, the generated enum types will be enum-ish object with `as const`.
352
+
353
+ - With `--ts_proto_opt=useExactTypes=false`, the generated `fromPartial` method will not use Exact types.
354
+
355
+ The default behavior is `useExactTypes=true`, which makes `fromPartial` use Exact type for its argument to make TypeScript reject any unknown properties.
356
+
351
357
  ### Only Types
352
358
 
353
359
  If you're looking for `ts-proto` to generate only types for your Protobuf types then passing all three of `outputEncodeMethods`, `outputJsonMethods`, and `outputClientImpl` as `false` is probably what you want, i.e.:
package/build/enums.js CHANGED
@@ -13,17 +13,30 @@ function generateEnum(ctx, fullName, enumDesc, sourceInfo) {
13
13
  const { options } = ctx;
14
14
  const chunks = [];
15
15
  utils_1.maybeAddComment(sourceInfo, chunks, (_a = enumDesc.options) === null || _a === void 0 ? void 0 : _a.deprecated);
16
- chunks.push(ts_poet_1.code `export ${options.constEnums ? 'const ' : ''}enum ${ts_poet_1.def(fullName)} {`);
16
+ if (options.enumsAsLiterals) {
17
+ chunks.push(ts_poet_1.code `export const ${ts_poet_1.def(fullName)} = {`);
18
+ }
19
+ else {
20
+ chunks.push(ts_poet_1.code `export ${options.constEnums ? 'const ' : ''}enum ${ts_poet_1.def(fullName)} {`);
21
+ }
22
+ const delimiter = options.enumsAsLiterals ? ':' : '=';
17
23
  enumDesc.value.forEach((valueDesc, index) => {
18
24
  var _a;
19
25
  const info = sourceInfo.lookup(sourceInfo_1.Fields.enum.value, index);
20
26
  utils_1.maybeAddComment(info, chunks, (_a = valueDesc.options) === null || _a === void 0 ? void 0 : _a.deprecated, `${valueDesc.name} - `);
21
- chunks.push(ts_poet_1.code `${valueDesc.name} = ${options.stringEnums ? `"${valueDesc.name}"` : valueDesc.number.toString()},`);
27
+ chunks.push(ts_poet_1.code `${valueDesc.name} ${delimiter} ${options.stringEnums ? `"${valueDesc.name}"` : valueDesc.number.toString()},`);
22
28
  });
23
29
  if (options.unrecognizedEnum)
24
30
  chunks.push(ts_poet_1.code `
25
- ${UNRECOGNIZED_ENUM_NAME} = ${options.stringEnums ? `"${UNRECOGNIZED_ENUM_NAME}"` : UNRECOGNIZED_ENUM_VALUE.toString()},`);
26
- chunks.push(ts_poet_1.code `}`);
31
+ ${UNRECOGNIZED_ENUM_NAME} ${delimiter} ${options.stringEnums ? `"${UNRECOGNIZED_ENUM_NAME}"` : UNRECOGNIZED_ENUM_VALUE.toString()},`);
32
+ if (options.enumsAsLiterals) {
33
+ chunks.push(ts_poet_1.code `} as const`);
34
+ chunks.push(ts_poet_1.code `\n`);
35
+ chunks.push(ts_poet_1.code `export type ${ts_poet_1.def(fullName)} = typeof ${ts_poet_1.def(fullName)}[keyof typeof ${ts_poet_1.def(fullName)}]`);
36
+ }
37
+ else {
38
+ chunks.push(ts_poet_1.code `}`);
39
+ }
27
40
  if (options.outputJsonMethods || (options.stringEnums && options.outputEncodeMethods)) {
28
41
  chunks.push(ts_poet_1.code `\n`);
29
42
  chunks.push(generateEnumFromJson(ctx, fullName, enumDesc));
package/build/main.js CHANGED
@@ -70,7 +70,7 @@ function generateFile(ctx, fileDesc) {
70
70
  // then add the encoder/decoder/base instance
71
71
  visit_1.visit(fileDesc, sourceInfo, (fullName, message, sInfo, fullProtoTypeName) => {
72
72
  const fullTypeName = utils_1.maybePrefixPackage(fileDesc, fullProtoTypeName);
73
- chunks.push(generateBaseInstance(ctx, fullName, message, fullTypeName));
73
+ chunks.push(generateBaseInstanceFactory(ctx, fullName, message, fullTypeName));
74
74
  const staticMembers = [];
75
75
  if (options.outputTypeRegistry) {
76
76
  staticMembers.push(ts_poet_1.code `$type: '${fullTypeName}' as const`);
@@ -86,8 +86,8 @@ function generateFile(ctx, fileDesc) {
86
86
  if (options.outputPartialMethods) {
87
87
  staticMembers.push(generateFromPartial(ctx, fullName, message));
88
88
  }
89
- staticMembers.push(...generateWrap(fullTypeName));
90
- staticMembers.push(...generateUnwrap(fullTypeName));
89
+ staticMembers.push(...generateWrap(ctx, fullTypeName));
90
+ staticMembers.push(...generateUnwrap(ctx, fullTypeName));
91
91
  chunks.push(ts_poet_1.code `
92
92
  export const ${ts_poet_1.def(fullName)} = {
93
93
  ${ts_poet_1.joinCode(staticMembers, { on: ',\n\n' })}
@@ -195,6 +195,7 @@ function makeUtils(options) {
195
195
  ...makeDeepPartial(options, longs),
196
196
  ...makeTimestampMethods(options, longs),
197
197
  ...longs,
198
+ ...makeComparisonUtils(),
198
199
  };
199
200
  }
200
201
  exports.makeUtils = makeUtils;
@@ -396,6 +397,17 @@ function makeTimestampMethods(options, longs) {
396
397
  `);
397
398
  return { toTimestamp, fromTimestamp, fromJsonTimestamp };
398
399
  }
400
+ function makeComparisonUtils() {
401
+ const isObject = ts_poet_1.conditionalOutput('isObject', ts_poet_1.code `
402
+ function isObject(value: any): boolean {
403
+ return typeof value === 'object' && value !== null;
404
+ }`);
405
+ const isSet = ts_poet_1.conditionalOutput('isSet', ts_poet_1.code `
406
+ function isSet(value: any): boolean {
407
+ return value !== null && value !== undefined;
408
+ }`);
409
+ return { isObject, isSet };
410
+ }
399
411
  // Create the interface with properties
400
412
  function generateInterfaceDeclaration(ctx, fullName, messageDesc, sourceInfo, fullTypeName) {
401
413
  var _a;
@@ -460,20 +472,39 @@ function generateOneofProperty(ctx, messageDesc, oneofIndex, sourceInfo) {
460
472
  return prop;
461
473
  */
462
474
  }
463
- // Create a 'base' instance with default values for decode to use as a prototype
464
- function generateBaseInstance(ctx, fullName, messageDesc, fullTypeName) {
465
- const fields = messageDesc.field
466
- .filter((field) => !types_1.isWithinOneOf(field))
467
- .map((field) => [field, types_1.defaultValue(ctx, field)])
468
- .filter(([field, val]) => val !== 'undefined' && !types_1.isBytes(field))
469
- .map(([field, val]) => {
475
+ // Create a function that constructs 'base' instance with default values for decode to use as a prototype
476
+ function generateBaseInstanceFactory(ctx, fullName, messageDesc, fullTypeName) {
477
+ const fields = [];
478
+ // When oneof=unions, we generate a single property with an ADT per `oneof` clause.
479
+ const processedOneofs = new Set();
480
+ for (const field of messageDesc.field) {
481
+ if (types_1.isWithinOneOfThatShouldBeUnion(ctx.options, field)) {
482
+ const { oneofIndex } = field;
483
+ if (!processedOneofs.has(oneofIndex)) {
484
+ processedOneofs.add(oneofIndex);
485
+ const name = case_1.maybeSnakeToCamel(messageDesc.oneofDecl[oneofIndex].name, ctx.options);
486
+ fields.push(ts_poet_1.code `${name}: undefined`);
487
+ }
488
+ continue;
489
+ }
470
490
  const name = case_1.maybeSnakeToCamel(field.name, ctx.options);
471
- return ts_poet_1.code `${name}: ${val}`;
472
- });
491
+ const val = types_1.isWithinOneOf(field)
492
+ ? 'undefined'
493
+ : types_1.isMapType(ctx, messageDesc, field)
494
+ ? '{}'
495
+ : types_1.isRepeated(field)
496
+ ? '[]'
497
+ : types_1.defaultValue(ctx, field);
498
+ fields.push(ts_poet_1.code `${name}: ${val}`);
499
+ }
473
500
  if (ctx.options.outputTypeRegistry) {
474
501
  fields.unshift(ts_poet_1.code `$type: '${fullTypeName}'`);
475
502
  }
476
- return ts_poet_1.code `const base${fullName}: object = { ${ts_poet_1.joinCode(fields, { on: ',' })} };`;
503
+ return ts_poet_1.code `
504
+ function createBase${fullName}(): ${fullName} {
505
+ return { ${ts_poet_1.joinCode(fields, { on: ',' })} };
506
+ }
507
+ `;
477
508
  }
478
509
  /** Creates a function to decode a message by loop overing the tags. */
479
510
  function generateDecode(ctx, fullName, messageDesc) {
@@ -487,22 +518,8 @@ function generateDecode(ctx, fullName, messageDesc) {
487
518
  ): ${fullName} {
488
519
  const reader = input instanceof ${Reader} ? input : new ${Reader}(input);
489
520
  let end = length === undefined ? reader.len : reader.pos + length;
490
- const message = { ...base${fullName} } as ${fullName};
521
+ const message = createBase${fullName}();
491
522
  `);
492
- // initialize all lists
493
- messageDesc.field.filter(types_1.isRepeated).forEach((field) => {
494
- const name = case_1.maybeSnakeToCamel(field.name, options);
495
- const value = types_1.isMapType(ctx, messageDesc, field) ? '{}' : '[]';
496
- chunks.push(ts_poet_1.code `message.${name} = ${value};`);
497
- });
498
- // initialize all buffers
499
- messageDesc.field
500
- .filter((field) => !types_1.isRepeated(field) && !types_1.isWithinOneOf(field) && types_1.isBytes(field))
501
- .forEach((field) => {
502
- const value = options.env === options_1.EnvOption.NODE ? 'Buffer.alloc(0)' : 'new Uint8Array()';
503
- const name = case_1.maybeSnakeToCamel(field.name, options);
504
- chunks.push(ts_poet_1.code `message.${name} = ${value};`);
505
- });
506
523
  // start the tag loop
507
524
  chunks.push(ts_poet_1.code `
508
525
  while (reader.pos < end) {
@@ -567,28 +584,29 @@ function generateDecode(ctx, fullName, messageDesc) {
567
584
  }
568
585
  // and then use the snippet to handle repeated fields if necessary
569
586
  if (types_1.isRepeated(field)) {
587
+ const maybeNonNullAssertion = ctx.options.useOptionals === 'all' ? '!' : '';
570
588
  if (types_1.isMapType(ctx, messageDesc, field)) {
571
589
  // We need a unique const within the `cast` statement
572
590
  const varName = `entry${field.number}`;
573
591
  chunks.push(ts_poet_1.code `
574
592
  const ${varName} = ${readSnippet};
575
593
  if (${varName}.value !== undefined) {
576
- message.${fieldName}[${varName}.key] = ${varName}.value;
594
+ message.${fieldName}${maybeNonNullAssertion}[${varName}.key] = ${varName}.value;
577
595
  }
578
596
  `);
579
597
  }
580
598
  else if (types_1.packedType(field.type) === undefined) {
581
- chunks.push(ts_poet_1.code `message.${fieldName}.push(${readSnippet});`);
599
+ chunks.push(ts_poet_1.code `message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});`);
582
600
  }
583
601
  else {
584
602
  chunks.push(ts_poet_1.code `
585
603
  if ((tag & 7) === 2) {
586
604
  const end2 = reader.uint32() + reader.pos;
587
605
  while (reader.pos < end2) {
588
- message.${fieldName}.push(${readSnippet});
606
+ message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});
589
607
  }
590
608
  } else {
591
- message.${fieldName}.push(${readSnippet});
609
+ message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});
592
610
  }
593
611
  `);
594
612
  }
@@ -799,13 +817,13 @@ function generateFromJson(ctx, fullName, messageDesc) {
799
817
  // create the basic function declaration
800
818
  chunks.push(ts_poet_1.code `
801
819
  fromJSON(${messageDesc.field.length > 0 ? 'object' : '_'}: any): ${fullName} {
802
- const message = { ...base${fullName} } as ${fullName};
820
+ const message = createBase${fullName}();
803
821
  `);
804
822
  // add a check for each incoming field
805
823
  messageDesc.field.forEach((field) => {
806
824
  const fieldName = case_1.maybeSnakeToCamel(field.name, options);
807
825
  const jsonName = utils_1.determineFieldJsonName(field, options);
808
- // get a generic 'reader.doSomething' bit that is specific to the basic type
826
+ // get code that extracts value from incoming object
809
827
  const readSnippet = (from) => {
810
828
  if (types_1.isEnum(field)) {
811
829
  const fromJson = types_1.getEnumMethod(ctx, field.typeName, 'FromJSON');
@@ -840,6 +858,9 @@ function generateFromJson(ctx, fullName, messageDesc) {
840
858
  else if (types_1.isAnyValueType(field) || types_1.isStructType(field)) {
841
859
  return ts_poet_1.code `${from}`;
842
860
  }
861
+ else if (types_1.isListValueType(field)) {
862
+ return ts_poet_1.code `[...${from}]`;
863
+ }
843
864
  else if (types_1.isValueType(ctx, field)) {
844
865
  const valueType = types_1.valueTypeName(ctx, field.typeName);
845
866
  if (types_1.isLongValueType(field) && options.forceLong === options_1.LongOption.LONG) {
@@ -848,9 +869,6 @@ function generateFromJson(ctx, fullName, messageDesc) {
848
869
  else if (types_1.isBytesValueType(field)) {
849
870
  return ts_poet_1.code `new ${case_1.capitalize(valueType.toCodeString())}(${from})`;
850
871
  }
851
- else if (types_1.isListValueType(field)) {
852
- return ts_poet_1.code `[...${from}]`;
853
- }
854
872
  else {
855
873
  return ts_poet_1.code `${case_1.capitalize(valueType.toCodeString())}(${from})`;
856
874
  }
@@ -917,22 +935,23 @@ function generateFromJson(ctx, fullName, messageDesc) {
917
935
  acc[${i}] = ${readSnippet('value')};
918
936
  return acc;
919
937
  }, {});
920
- `);
921
- }
922
- else if (types_1.isAnyValueType(field)) {
923
- chunks.push(ts_poet_1.code `
924
- message.${fieldName} = Array.isArray(object?.${jsonName}) ? [...object.${jsonName}] : [];
925
938
  `);
926
939
  }
927
940
  else {
928
- // Explicit `any` type required to make TS with noImplicitAny happy. `object` is also `any` here.
929
- chunks.push(ts_poet_1.code `
930
- message.${fieldName} = (object.${jsonName} ?? []).map((e: any) => ${readSnippet('e')});
931
- `);
941
+ const readValueSnippet = readSnippet('e');
942
+ if (readValueSnippet.toString() === ts_poet_1.code `e`.toString()) {
943
+ chunks.push(ts_poet_1.code `message.${fieldName} = Array.isArray(object?.${jsonName}) ? [...object.${jsonName}] : [];`);
944
+ }
945
+ else {
946
+ // Explicit `any` type required to make TS with noImplicitAny happy. `object` is also `any` here.
947
+ chunks.push(ts_poet_1.code `
948
+ message.${fieldName} = Array.isArray(object?.${jsonName}) ? object.${jsonName}.map((e: any) => ${readValueSnippet}): [];
949
+ `);
950
+ }
932
951
  }
933
952
  }
934
953
  else if (types_1.isWithinOneOfThatShouldBeUnion(options, field)) {
935
- chunks.push(ts_poet_1.code `if (object.${jsonName} !== undefined && object.${jsonName} !== null) {`);
954
+ chunks.push(ts_poet_1.code `if (${ctx.utils.isSet}(object.${jsonName})) {`);
936
955
  const oneofName = case_1.maybeSnakeToCamel(messageDesc.oneofDecl[field.oneofIndex].name, options);
937
956
  chunks.push(ts_poet_1.code `
938
957
  message.${oneofName} = { $case: '${fieldName}', ${fieldName}: ${readSnippet(`object.${jsonName}`)} }
@@ -940,22 +959,27 @@ function generateFromJson(ctx, fullName, messageDesc) {
940
959
  chunks.push(ts_poet_1.code `}`);
941
960
  }
942
961
  else if (types_1.isAnyValueType(field)) {
943
- chunks.push(ts_poet_1.code `message.${fieldName} = object.${fieldName};`);
962
+ chunks.push(ts_poet_1.code `message.${fieldName} = ${ctx.utils.isSet}(object?.${jsonName})
963
+ ? ${readSnippet(`object.${jsonName}`)}
964
+ : undefined;
965
+ `);
944
966
  }
945
967
  else if (types_1.isStructType(field)) {
946
- chunks.push(ts_poet_1.code `message.${fieldName} = typeof(object.${fieldName}) === 'object' ? object.${fieldName} : undefined;`);
968
+ chunks.push(ts_poet_1.code `message.${fieldName} = ${ctx.utils.isObject}(object.${jsonName})
969
+ ? ${readSnippet(`object.${jsonName}`)}
970
+ : undefined;`);
947
971
  }
948
972
  else if (types_1.isListValueType(field)) {
949
973
  chunks.push(ts_poet_1.code `
950
- message.${fieldName} = Array.isArray(object?.${fieldName})
951
- ? ${readSnippet(`object.${fieldName}`)}
974
+ message.${fieldName} = Array.isArray(object.${jsonName})
975
+ ? ${readSnippet(`object.${jsonName}`)}
952
976
  : ${'undefined'};
953
977
  `);
954
978
  }
955
979
  else {
956
980
  const fallback = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
957
981
  chunks.push(ts_poet_1.code `
958
- message.${fieldName} = (object.${jsonName} !== undefined && object.${jsonName} !== null)
982
+ message.${fieldName} = ${ctx.utils.isSet}(object.${jsonName})
959
983
  ? ${readSnippet(`object.${jsonName}`)}
960
984
  : ${fallback};
961
985
  `);
@@ -1095,13 +1119,19 @@ function generateToJson(ctx, fullName, messageDesc) {
1095
1119
  function generateFromPartial(ctx, fullName, messageDesc) {
1096
1120
  const { options, utils, typeMap } = ctx;
1097
1121
  const chunks = [];
1098
- const Timestamp = utils_1.impProto(options, 'google/protobuf/timestamp', 'Timestamp');
1099
1122
  // create the basic function declaration
1100
1123
  const paramName = messageDesc.field.length > 0 ? 'object' : '_';
1101
- chunks.push(ts_poet_1.code `
1102
- fromPartial<I extends ${utils.Exact}<${utils.DeepPartial}<${fullName}>, I>>(${paramName}: I): ${fullName} {
1103
- const message = { ...base${fullName} } as ${fullName};
1104
- `);
1124
+ if (ctx.options.useExactTypes) {
1125
+ chunks.push(ts_poet_1.code `
1126
+ fromPartial<I extends ${utils.Exact}<${utils.DeepPartial}<${fullName}>, I>>(${paramName}: I): ${fullName} {
1127
+ `);
1128
+ }
1129
+ else {
1130
+ chunks.push(ts_poet_1.code `
1131
+ fromPartial(${paramName}: ${utils.DeepPartial}<${fullName}>): ${fullName} {
1132
+ `);
1133
+ }
1134
+ chunks.push(ts_poet_1.code `const message = createBase${fullName}();`);
1105
1135
  // add a check for each incoming field
1106
1136
  messageDesc.field.forEach((field) => {
1107
1137
  const fieldName = case_1.maybeSnakeToCamel(field.name, options);
@@ -1211,7 +1241,7 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1211
1241
  chunks.push(ts_poet_1.code `}`);
1212
1242
  return ts_poet_1.joinCode(chunks, { on: '\n' });
1213
1243
  }
1214
- function generateWrap(fullProtoTypeName) {
1244
+ function generateWrap(ctx, fullProtoTypeName) {
1215
1245
  const chunks = [];
1216
1246
  if (types_1.isStructTypeName(fullProtoTypeName)) {
1217
1247
  chunks.push(ts_poet_1.code `wrap(object: {[key: string]: any} | undefined): Struct {
@@ -1225,7 +1255,29 @@ function generateWrap(fullProtoTypeName) {
1225
1255
  }`);
1226
1256
  }
1227
1257
  if (types_1.isAnyValueTypeName(fullProtoTypeName)) {
1228
- chunks.push(ts_poet_1.code `wrap(value: any): Value {
1258
+ if (ctx.options.oneof === options_1.OneofOption.UNIONS) {
1259
+ chunks.push(ts_poet_1.code `wrap(value: any): Value {
1260
+ if (value === null) {
1261
+ return {kind: {$case: 'nullValue', nullValue: NullValue.NULL_VALUE}};
1262
+ } else if (typeof value === 'boolean') {
1263
+ return {kind: {$case: 'boolValue', boolValue: value}};
1264
+ } else if (typeof value === 'number') {
1265
+ return {kind: {$case: 'numberValue', numberValue: value}};
1266
+ } else if (typeof value === 'string') {
1267
+ return {kind: {$case: 'stringValue', stringValue: value}};
1268
+ } else if (Array.isArray(value)) {
1269
+ return {kind: {$case: 'listValue', listValue: value}};
1270
+ } else if (typeof value === 'object') {
1271
+ return {kind: {$case: 'structValue', structValue: value}};
1272
+ } else if (typeof value === 'undefined') {
1273
+ return {} as Value;
1274
+ } else {
1275
+ throw new Error('Unsupported any value type: ' + typeof value);
1276
+ }
1277
+ }`);
1278
+ }
1279
+ else {
1280
+ chunks.push(ts_poet_1.code `wrap(value: any): Value {
1229
1281
  if (value === null) {
1230
1282
  return {nullValue: NullValue.NULL_VALUE} as Value;
1231
1283
  } else if (typeof value === 'boolean') {
@@ -1244,15 +1296,16 @@ function generateWrap(fullProtoTypeName) {
1244
1296
  throw new Error('Unsupported any value type: ' + typeof value);
1245
1297
  }
1246
1298
  }`);
1299
+ }
1247
1300
  }
1248
1301
  if (types_1.isListValueTypeName(fullProtoTypeName)) {
1249
- chunks.push(ts_poet_1.code `wrap(value: Array<any>): ListValue {
1250
- return {values: value};
1302
+ chunks.push(ts_poet_1.code `wrap(value: Array<any> | undefined): ListValue {
1303
+ return {values: value ?? []};
1251
1304
  }`);
1252
1305
  }
1253
1306
  return chunks;
1254
1307
  }
1255
- function generateUnwrap(fullProtoTypeName) {
1308
+ function generateUnwrap(ctx, fullProtoTypeName) {
1256
1309
  const chunks = [];
1257
1310
  if (types_1.isStructTypeName(fullProtoTypeName)) {
1258
1311
  chunks.push(ts_poet_1.code `unwrap(message: Struct): {[key: string]: any} {
@@ -1264,7 +1317,27 @@ function generateUnwrap(fullProtoTypeName) {
1264
1317
  }`);
1265
1318
  }
1266
1319
  if (types_1.isAnyValueTypeName(fullProtoTypeName)) {
1267
- chunks.push(ts_poet_1.code `unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined {
1320
+ if (ctx.options.oneof === options_1.OneofOption.UNIONS) {
1321
+ chunks.push(ts_poet_1.code `unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined {
1322
+ if (message.kind?.$case === 'nullValue') {
1323
+ return null;
1324
+ } else if (message.kind?.$case === 'numberValue') {
1325
+ return message.kind?.numberValue;
1326
+ } else if (message.kind?.$case === 'stringValue') {
1327
+ return message.kind?.stringValue;
1328
+ } else if (message.kind?.$case === 'boolValue') {
1329
+ return message.kind?.boolValue;
1330
+ } else if (message.kind?.$case === 'structValue') {
1331
+ return message.kind?.structValue;
1332
+ } else if (message.kind?.$case === 'listValue') {
1333
+ return message.kind?.listValue;
1334
+ } else {
1335
+ return undefined;
1336
+ }
1337
+ }`);
1338
+ }
1339
+ else {
1340
+ chunks.push(ts_poet_1.code `unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined {
1268
1341
  if (message?.stringValue !== undefined) {
1269
1342
  return message.stringValue;
1270
1343
  } else if (message?.numberValue !== undefined) {
@@ -1280,6 +1353,7 @@ function generateUnwrap(fullProtoTypeName) {
1280
1353
  }
1281
1354
  return undefined;
1282
1355
  }`);
1356
+ }
1283
1357
  }
1284
1358
  if (types_1.isListValueTypeName(fullProtoTypeName)) {
1285
1359
  chunks.push(ts_poet_1.code `unwrap(message: ListValue): Array<any> {
package/build/options.js CHANGED
@@ -48,6 +48,7 @@ function defaultOptions() {
48
48
  outputTypeRegistry: false,
49
49
  stringEnums: false,
50
50
  constEnums: false,
51
+ enumsAsLiterals: false,
51
52
  outputClientImpl: true,
52
53
  outputServices: ServiceOption.DEFAULT,
53
54
  returnObservable: false,
@@ -60,6 +61,7 @@ function defaultOptions() {
60
61
  outputSchema: false,
61
62
  onlyTypes: false,
62
63
  emitImportedFiles: true,
64
+ useExactTypes: true,
63
65
  };
64
66
  }
65
67
  exports.defaultOptions = defaultOptions;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.95.1",
3
+ "version": "1.97.1",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",