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 +43 -7
- package/build/generate-struct-wrappers.js +26 -0
- package/build/main.js +21 -9
- package/build/options.d.ts +2 -1
- package/build/options.js +1 -0
- package/build/types.js +3 -1
- package/build/utils.d.ts +1 -0
- package/build/utils.js +5 -1
- package/package.json +1 -1
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";
|
|
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
|
-
|
|
809
|
-
|
|
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
|
-
|
|
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}', ${
|
|
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}.${
|
|
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}', ${
|
|
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}.${
|
|
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
|
|
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}?.${
|
|
2156
|
-
&& ${oneofNameWithObject}?.${
|
|
2167
|
+
&& ${oneofNameWithObject}?.${valueName} !== undefined
|
|
2168
|
+
&& ${oneofNameWithObject}?.${valueName} !== null
|
|
2157
2169
|
) {
|
|
2158
|
-
${oneofNameWithMessage} = { $case: '${fieldName}', ${
|
|
2170
|
+
${oneofNameWithMessage} = { $case: '${fieldName}', ${valueName}: ${v} };
|
|
2159
2171
|
}
|
|
2160
2172
|
`);
|
|
2161
2173
|
}
|
package/build/options.d.ts
CHANGED
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) &&
|
|
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
|
}
|