ts-proto 1.179.0 → 1.180.0

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/README.markdown CHANGED
@@ -754,7 +754,7 @@ ts-protoc --ts_proto_out=./output -I=./protos ./protoc/*.proto
754
754
  # Todo
755
755
 
756
756
  - Support the string-based encoding of duration in `fromJSON`/`toJSON`
757
- - Make `oneof=unions` the default behavior in 2.0
757
+ - Make `oneof=unions-value` the default behavior in 2.0
758
758
  - Probably change `forceLong` default in 2.0, should default to `forceLong=long`
759
759
  - Make `esModuleInterop=true` the default in 2.0
760
760
 
@@ -772,11 +772,11 @@ Will generate a `Foo` type with two fields: `field_a: string | undefined;` and `
772
772
 
773
773
  With this output, you'll have to check both `if object.field_a` and `if object.field_b`, and if you set one, you'll have to remember to unset the other.
774
774
 
775
- Instead, we recommend using the `oneof=unions` option, which will change the output to be an Abstract Data Type/ADT like:
775
+ Instead, we recommend using the `oneof=unions-value` option, which will change the output to be an Abstract Data Type/ADT like:
776
776
 
777
777
  ```typescript
778
778
  interface YourMessage {
779
- eitherField?: { $case: "field_a"; field_a: string } | { $case: "field_b"; field_b: string };
779
+ eitherField?: { $case: "field_a"; value: string } | { $case: "field_b"; value: string };
780
780
  }
781
781
  ```
782
782
 
@@ -784,11 +784,21 @@ As this will automatically enforce only one of `field_a` or `field_b` "being set
784
784
 
785
785
  (Note that `eitherField` is optional b/c `oneof` in Protobuf means "at most one field" is set, and does not mean one of the fields _must_ be set.)
786
786
 
787
- In ts-proto's currently-unscheduled 2.x release, `oneof=unions` will become the default behavior.
787
+ In ts-proto's currently-unscheduled 2.x release, `oneof=unions-value` will become the default behavior.
788
+
789
+ There is also a `oneof=unions` option, which generates a union where the field names are included in each option:
790
+
791
+ ```typescript
792
+ interface YourMessage {
793
+ eitherField?: { $case: "field_a"; field_a: string } | { $case: "field_b"; field_b: string };
794
+ }
795
+ ```
796
+
797
+ This is no longer recommended as it can be difficult to write code and types to handle multiple oneof options:
788
798
 
789
799
  ## OneOf Type Helpers
790
800
 
791
- The following helper types may make it easier to work with the types generated from `oneof=unions`:
801
+ The following helper types may make it easier to work with the types generated from `oneof=unions`, though they are generally not needed if you use `oneof=unions-value`:
792
802
 
793
803
  ```ts
794
804
  /** Extracts all the case names from a oneOf field. */
@@ -799,19 +809,45 @@ type OneOfValues<T> = T extends { $case: infer U extends string; [key: string]:
799
809
 
800
810
  /** Extracts the specific type of a oneOf case based on its field name */
801
811
  type OneOfCase<T, K extends OneOfCases<T>> = T extends {
812
+ $case: K;
813
+ [key: string]: unknown;
814
+ }
815
+ ? T
816
+ : never;
817
+
818
+ /** Extracts the specific type of a value type from a oneOf field */
819
+ type OneOfValue<T, K extends OneOfCases<T>> = T extends {
802
820
  $case: infer U extends K;
803
821
  [key: string]: unknown;
804
822
  }
805
823
  ? T[U]
806
824
  : never;
825
+ ```
807
826
 
808
- /** Extracts the specific type of a value type from a oneOf field */
809
- export type OneOfValue<T, K extends OneOfCases<T>> = T extends {
827
+ For comparison, the equivalents for `oneof=unions-value`:
828
+
829
+ ```ts
830
+ /** Extracts all the case names from a oneOf field. */
831
+ type OneOfCases<T> = T['$case'];
832
+
833
+ /** Extracts a union of all the value types from a oneOf field */
834
+ type OneOfValues<T> = T['value'];
835
+
836
+ /** Extracts the specific type of a oneOf case based on its field name */
837
+ type OneOfCase<T, K extends OneOfCases<T>> = T extends {
810
838
  $case: K;
811
839
  [key: string]: unknown;
812
840
  }
813
841
  ? T
814
842
  : never;
843
+
844
+ /** Extracts the specific type of a value type from a oneOf field */
845
+ type OneOfValue<T, K extends OneOfCases<T>> = T extends {
846
+ $case: infer U extends K;
847
+ value: unknown;
848
+ }
849
+ ? T[U]
850
+ : never;
815
851
  ```
816
852
 
817
853
  # Default values and unset fields
@@ -193,6 +193,27 @@ function generateWrapShallow(ctx, fullProtoTypeName, fieldNames) {
193
193
  throw new ${ctx.utils.globalThis}.Error('Unsupported any value type: ' + typeof value);
194
194
  }
195
195
  return result;
196
+ }`);
197
+ }
198
+ else if (ctx.options.oneof === options_1.OneofOption.UNIONS_VALUE) {
199
+ chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
200
+ const result = createBaseValue()${maybeAsAny(ctx.options)};
201
+ if (value === null) {
202
+ result.kind = {$case: '${fieldNames.nullValue}', value };
203
+ } else if (typeof value === 'boolean') {
204
+ result.kind = {$case: '${fieldNames.boolValue}', value };
205
+ } else if (typeof value === 'number') {
206
+ result.kind = {$case: '${fieldNames.numberValue}', value };
207
+ } else if (typeof value === 'string') {
208
+ result.kind = {$case: '${fieldNames.stringValue}', value };
209
+ } else if (${ctx.utils.globalThis}.Array.isArray(value)) {
210
+ result.kind = {$case: '${fieldNames.listValue}', value };
211
+ } else if (typeof value === 'object') {
212
+ result.kind = {$case: '${fieldNames.structValue}', value };
213
+ } else if (typeof value !== 'undefined') {
214
+ throw new ${ctx.utils.globalThis}.Error('Unsupported any value type: ' + typeof value);
215
+ }
216
+ return result;
196
217
  }`);
197
218
  }
198
219
  else {
@@ -284,6 +305,11 @@ function generateUnwrapShallow(ctx, fullProtoTypeName, fieldNames) {
284
305
  } else {
285
306
  return undefined;
286
307
  }
308
+ }`);
309
+ }
310
+ else if (ctx.options.oneof === options_1.OneofOption.UNIONS_VALUE) {
311
+ chunks.push((0, ts_poet_1.code) `unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined {
312
+ return message.kind?.value;
287
313
  }`);
288
314
  }
289
315
  else {
package/build/main.js CHANGED
@@ -448,6 +448,12 @@ function makeDeepPartial(options, longs) {
448
448
  ? { [K in keyof Omit<T, '$case'>]?: DeepPartial<T[K]> } & { ${maybeReadonly(options)}$case: T['$case'] }
449
449
  `;
450
450
  }
451
+ else if (options.oneof === options_1.OneofOption.UNIONS_VALUE) {
452
+ oneofCase = `
453
+ : T extends { ${maybeReadonly(options)}$case: string; value: unknown; }
454
+ ? { ${maybeReadonly(options)}$case: T['$case']; value?: DeepPartial<T['value']>; }
455
+ `;
456
+ }
451
457
  const maybeExport = options.exportCommonSymbols ? "export" : "";
452
458
  // Allow passing longs as numbers or strings, nad we'll convert them
453
459
  const maybeLong = options.forceLong === options_1.LongOption.LONG ? (0, ts_poet_1.code) ` : T extends ${longs.Long} ? string | number | Long ` : "";
@@ -711,7 +717,8 @@ function generateOneofProperty(ctx, messageDesc, oneofIndex, sourceInfo) {
711
717
  const unionType = (0, ts_poet_1.joinCode)(fields.map((f) => {
712
718
  let fieldName = (0, case_1.maybeSnakeToCamel)(f.name, options);
713
719
  let typeName = (0, types_1.toTypeName)(ctx, messageDesc, f);
714
- return (0, ts_poet_1.code) `{ ${mbReadonly}$case: '${fieldName}', ${mbReadonly}${fieldName}: ${typeName} }`;
720
+ let valueName = (0, utils_1.oneofValueName)(fieldName, options);
721
+ return (0, ts_poet_1.code) `{ ${mbReadonly}$case: '${fieldName}', ${mbReadonly}${valueName}: ${typeName} }`;
715
722
  }), { on: " | " });
716
723
  const name = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[oneofIndex].name, options);
717
724
  return (0, ts_poet_1.code) `${mbReadonly}${name}?: ${unionType} | ${(0, utils_1.nullOrUndefined)(options)},`;
@@ -979,9 +986,10 @@ function generateDecode(ctx, fullName, messageDesc) {
979
986
  const oneofNameWithMessage = options.useJsonName
980
987
  ? messageProperty
981
988
  : (0, utils_1.getPropertyAccessor)("message", (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options));
989
+ const valueName = (0, utils_1.oneofValueName)(fieldName, options);
982
990
  chunks.push((0, ts_poet_1.code) `
983
991
  ${tagCheck}
984
- ${oneofNameWithMessage} = { $case: '${fieldName}', ${fieldName}: ${readSnippet} };
992
+ ${oneofNameWithMessage} = { $case: '${fieldName}', ${valueName}: ${readSnippet} };
985
993
  `);
986
994
  }
987
995
  else {
@@ -1267,8 +1275,9 @@ function generateEncode(ctx, fullName, messageDesc) {
1267
1275
  for (const oneOfField of oneOfFieldsDict[field.oneofIndex]) {
1268
1276
  const writeSnippet = getEncodeWriteSnippet(ctx, oneOfField);
1269
1277
  const oneOfFieldName = (0, case_1.maybeSnakeToCamel)(oneOfField.name, ctx.options);
1278
+ const valueName = (0, utils_1.oneofValueName)(oneOfFieldName, ctx.options);
1270
1279
  chunks.push((0, ts_poet_1.code) `case "${oneOfFieldName}":
1271
- ${writeSnippet(`${oneofNameWithMessage}.${oneOfFieldName}`)};
1280
+ ${writeSnippet(`${oneofNameWithMessage}.${valueName}`)};
1272
1281
  break;`);
1273
1282
  }
1274
1283
  chunks.push((0, ts_poet_1.code) `}`);
@@ -1761,8 +1770,9 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1761
1770
  const fieldName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
1762
1771
  chunks.push((0, ts_poet_1.code) `${fieldName}: `);
1763
1772
  }
1773
+ const valueName = (0, utils_1.oneofValueName)(fieldKey, options);
1764
1774
  const ternaryIf = (0, ts_poet_1.code) `${ctx.utils.isSet}(${jsonProperty})`;
1765
- const ternaryThen = (0, ts_poet_1.code) `{ $case: '${fieldName}', ${fieldKey}: ${readSnippet(`${jsonProperty}`)}`;
1775
+ const ternaryThen = (0, ts_poet_1.code) `{ $case: '${fieldName}', ${valueName}: ${readSnippet(`${jsonProperty}`)}`;
1766
1776
  chunks.push((0, ts_poet_1.code) `${ternaryIf} ? ${ternaryThen}} : `);
1767
1777
  if (field === lastCase) {
1768
1778
  chunks.push((0, ts_poet_1.code) `${(0, utils_1.nullOrUndefined)(options)},`);
@@ -1972,9 +1982,10 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1972
1982
  const oneofNameWithMessage = options.useJsonName
1973
1983
  ? messageProperty
1974
1984
  : (0, utils_1.getPropertyAccessor)("message", (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options));
1985
+ const valueName = (0, utils_1.oneofValueName)(fieldName, options);
1975
1986
  chunks.push((0, ts_poet_1.code) `
1976
1987
  if (${oneofNameWithMessage}?.$case === '${fieldName}') {
1977
- ${jsonProperty} = ${readSnippet(`${oneofNameWithMessage}.${fieldName}`)};
1988
+ ${jsonProperty} = ${readSnippet(`${oneofNameWithMessage}.${valueName}`)};
1978
1989
  }
1979
1990
  `);
1980
1991
  }
@@ -2148,14 +2159,15 @@ function generateFromPartial(ctx, fullName, messageDesc) {
2148
2159
  const oneofName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
2149
2160
  const oneofNameWithMessage = (0, utils_1.getPropertyAccessor)("message", oneofName);
2150
2161
  const oneofNameWithObject = (0, utils_1.getPropertyAccessor)("object", oneofName);
2151
- const v = readSnippet(`${oneofNameWithObject}.${fieldName}`);
2162
+ const valueName = (0, utils_1.oneofValueName)(fieldName, options);
2163
+ const v = readSnippet(`${oneofNameWithObject}.${valueName}`);
2152
2164
  chunks.push((0, ts_poet_1.code) `
2153
2165
  if (
2154
2166
  ${oneofNameWithObject}?.$case === '${fieldName}'
2155
- && ${oneofNameWithObject}?.${fieldName} !== undefined
2156
- && ${oneofNameWithObject}?.${fieldName} !== null
2167
+ && ${oneofNameWithObject}?.${valueName} !== undefined
2168
+ && ${oneofNameWithObject}?.${valueName} !== null
2157
2169
  ) {
2158
- ${oneofNameWithMessage} = { $case: '${fieldName}', ${fieldName}: ${v} };
2170
+ ${oneofNameWithMessage} = { $case: '${fieldName}', ${valueName}: ${v} };
2159
2171
  }
2160
2172
  `);
2161
2173
  }
@@ -22,7 +22,8 @@ export declare enum EnvOption {
22
22
  }
23
23
  export declare enum OneofOption {
24
24
  PROPERTIES = "properties",
25
- UNIONS = "unions"
25
+ UNIONS = "unions",
26
+ UNIONS_VALUE = "unions-value"
26
27
  }
27
28
  export declare enum ServiceOption {
28
29
  GRPC = "grpc-js",
package/build/options.js CHANGED
@@ -30,6 +30,7 @@ var OneofOption;
30
30
  (function (OneofOption) {
31
31
  OneofOption["PROPERTIES"] = "properties";
32
32
  OneofOption["UNIONS"] = "unions";
33
+ OneofOption["UNIONS_VALUE"] = "unions-value";
33
34
  })(OneofOption || (exports.OneofOption = OneofOption = {}));
34
35
  var ServiceOption;
35
36
  (function (ServiceOption) {
package/build/types.js CHANGED
@@ -407,7 +407,9 @@ function isWithinOneOf(field) {
407
407
  }
408
408
  exports.isWithinOneOf = isWithinOneOf;
409
409
  function isWithinOneOfThatShouldBeUnion(options, field) {
410
- return isWithinOneOf(field) && options.oneof === options_1.OneofOption.UNIONS && !field.proto3Optional;
410
+ return (isWithinOneOf(field) &&
411
+ (options.oneof === options_1.OneofOption.UNIONS || options.oneof === options_1.OneofOption.UNIONS_VALUE) &&
412
+ !field.proto3Optional);
411
413
  }
412
414
  exports.isWithinOneOfThatShouldBeUnion = isWithinOneOfThatShouldBeUnion;
413
415
  function isRepeated(field) {
package/build/utils.d.ts CHANGED
@@ -73,6 +73,7 @@ export declare function arrowFunction(params: string, body: Code | string, isOne
73
73
  export declare function nullOrUndefined(options: Pick<Options, "useNullAsOptional">, hasProto3Optional?: boolean): string;
74
74
  export declare function maybeCheckIsNotNull(options: Pick<Options, "useNullAsOptional">, typeName: string, prefix?: string): string;
75
75
  export declare function maybeCheckIsNull(options: Pick<Options, "useNullAsOptional">, typeName: string, prefix?: string): string;
76
+ export declare function oneofValueName(fieldName: string, options: Pick<Options, "oneof">): string;
76
77
  export declare function withOrMaybeCheckIsNotNull(options: Pick<Options, "useNullAsOptional">, typeName: string): string;
77
78
  export declare function withOrMaybeCheckIsNull(options: Pick<Options, "useNullAsOptional">, typeName: string): string;
78
79
  export declare function withAndMaybeCheckIsNotNull(options: Pick<Options, "useNullAsOptional">, typeName: string): string;
package/build/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getVersions = exports.withAndMaybeCheckIsNull = exports.withAndMaybeCheckIsNotNull = exports.withOrMaybeCheckIsNull = exports.withOrMaybeCheckIsNotNull = exports.maybeCheckIsNull = exports.maybeCheckIsNotNull = exports.nullOrUndefined = exports.arrowFunction = exports.tryCatchBlock = exports.impProto = exports.impFile = exports.getPropertyAccessor = exports.safeAccessor = exports.getFieldName = exports.getFieldJsonName = exports.FormattedMethodDescriptor = exports.assertInstanceOf = exports.maybePrefixPackage = exports.maybeAddComment = exports.upperFirst = exports.lowerFirst = exports.singular = exports.fail = exports.readToBuffer = exports.generateIndexFiles = exports.protoFilesToGenerate = void 0;
3
+ exports.getVersions = exports.withAndMaybeCheckIsNull = exports.withAndMaybeCheckIsNotNull = exports.withOrMaybeCheckIsNull = exports.withOrMaybeCheckIsNotNull = exports.oneofValueName = exports.maybeCheckIsNull = exports.maybeCheckIsNotNull = exports.nullOrUndefined = exports.arrowFunction = exports.tryCatchBlock = exports.impProto = exports.impFile = exports.getPropertyAccessor = exports.safeAccessor = exports.getFieldName = exports.getFieldJsonName = exports.FormattedMethodDescriptor = exports.assertInstanceOf = exports.maybePrefixPackage = exports.maybeAddComment = exports.upperFirst = exports.lowerFirst = exports.singular = exports.fail = exports.readToBuffer = exports.generateIndexFiles = exports.protoFilesToGenerate = void 0;
4
4
  const path = require("path");
5
5
  const ts_poet_1 = require("ts-poet");
6
6
  const options_1 = require("./options");
@@ -266,6 +266,10 @@ function maybeCheckIsNull(options, typeName, prefix) {
266
266
  return options.useNullAsOptional ? ` ${prefix} ${typeName} === null` : "";
267
267
  }
268
268
  exports.maybeCheckIsNull = maybeCheckIsNull;
269
+ function oneofValueName(fieldName, options) {
270
+ return options.oneof === options_1.OneofOption.UNIONS ? fieldName : "value";
271
+ }
272
+ exports.oneofValueName = oneofValueName;
269
273
  function withOrMaybeCheckIsNotNull(options, typeName) {
270
274
  return maybeCheckIsNotNull(options, typeName, "||");
271
275
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.179.0",
3
+ "version": "1.180.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",