ts-proto 1.170.0 → 1.172.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
@@ -547,6 +547,58 @@ Generated code will be placed in the Gradle build directory.
547
547
 
548
548
  - With `--ts_proto_opt=comments=false`, comments won't be copied from the proto files to the generated code.
549
549
 
550
+ - With `--ts_proto_opt=useNullAsOptional=true`, `undefined` values will be converted to `null`, and if you use `optional` label in your `.proto` file, the field will have `undefined` type as well. for example:
551
+
552
+ ```protobuf
553
+ message ProfileInfo {
554
+ int32 id = 1;
555
+ string bio = 2;
556
+ string phone = 3;
557
+ }
558
+
559
+ message Department {
560
+ int32 id = 1;
561
+ string name = 2;
562
+ }
563
+
564
+ message User {
565
+ int32 id = 1;
566
+ string username = 2;
567
+ /*
568
+ ProfileInfo will be optional in typescript, the type will be ProfileInfo | null | undefined
569
+ this is needed in cases where you don't wanna provide any value for the profile.
570
+ */
571
+ optional ProfileInfo profile = 3;
572
+
573
+ /*
574
+ Department only accepts a Department type or null, so this means you have to pass it null if there is no value available.
575
+ */
576
+ Department department = 4;
577
+ }
578
+ ```
579
+
580
+ the generated interfaces will be:
581
+
582
+ ```typescript
583
+ export interface ProfileInfo {
584
+ id: number;
585
+ bio: string;
586
+ phone: string;
587
+ }
588
+
589
+ export interface Department {
590
+ id: number;
591
+ name: string;
592
+ }
593
+
594
+ export interface User {
595
+ id: number;
596
+ username: string;
597
+ profile?: ProfileInfo | null | undefined; // check this one
598
+ department: Department | null; // check this one
599
+ }
600
+ ```
601
+
550
602
  ### NestJS Support
551
603
 
552
604
  We have a great way of working together with [nestjs](https://docs.nestjs.com/microservices/grpc). `ts-proto` generates `interfaces` and `decorators` for you controller, client. For more information see the [nestjs readme](NESTJS.markdown).
@@ -724,6 +776,28 @@ As this will automatically enforce only one of `field_a` or `field_b` "being set
724
776
 
725
777
  In ts-proto's currently-unscheduled 2.x release, `oneof=unions` will become the default behavior.
726
778
 
779
+ ## OneOf Type Helpers
780
+
781
+ The following helper types may make it easier to work with the types generated from `oneof=unions`:
782
+
783
+ ```ts
784
+ /** Extracts all the case names from a oneOf field. */
785
+ type OneOfCases<T> = T extends { $case: infer U extends string } ? U : never;
786
+
787
+ /** Extracts a union of all the value types from a oneOf field */
788
+ type OneOfValues<T> = T extends { $case: infer U extends string; [key: string]: unknown }
789
+ ? T[U]
790
+ : never;
791
+
792
+ /** Extracts the specific type of a oneOf case based on its field name */
793
+ type OneOfCase<T, K extends OneOfCases<T>> = T extends {
794
+ $case: infer U extends K;
795
+ [key: string]: unknown;
796
+ }
797
+ ? T[U]
798
+ : never;
799
+ ```
800
+
727
801
  # Default values and unset fields
728
802
 
729
803
  In core Protobuf (and so also `ts-proto`), values that are _unset_ or equal to the default value are not sent over the wire.
@@ -0,0 +1,8 @@
1
+ import { Options } from "./options";
2
+ /** Converts `key` to TS/JS camel-case idiom, unless overridden not to. */
3
+ export declare function maybeSnakeToCamel(key: string, options: Pick<Options, "snakeToCamel">): string;
4
+ export declare function snakeToCamel(s: string): string;
5
+ export declare function camelToSnake(s: string): string;
6
+ export declare function capitalize(s: string): string;
7
+ export declare function uncapitalize(s: string): string;
8
+ export declare function camelCaseGrpc(s: string): string;
@@ -0,0 +1,19 @@
1
+ import { TypeMap } from "./types";
2
+ import { Utils } from "./main";
3
+ import { Options } from "./options";
4
+ import { FileDescriptorProto } from "ts-proto-descriptors";
5
+ /** Provides a parameter object for passing around the various context/config data. */
6
+ export interface BaseContext {
7
+ options: Options;
8
+ typeMap: TypeMap;
9
+ utils: Utils;
10
+ }
11
+ export interface Context extends BaseContext {
12
+ currentFile: FileContext;
13
+ }
14
+ export interface FileContext {
15
+ isProto3Syntax: boolean;
16
+ }
17
+ export declare function createFileContext(file: FileDescriptorProto): {
18
+ isProto3Syntax: boolean;
19
+ };
@@ -0,0 +1,4 @@
1
+ import { Context } from "./context";
2
+ import { Code } from "ts-poet";
3
+ export declare function generateEncoder(ctx: Context, typeName: string): Code;
4
+ export declare function generateDecoder(ctx: Context, typeName: string): Code;
@@ -0,0 +1,19 @@
1
+ import { Code } from "ts-poet";
2
+ import { EnumDescriptorProto, EnumValueDescriptorProto } from "ts-proto-descriptors";
3
+ import SourceInfo from "./sourceInfo";
4
+ import { Context } from "./context";
5
+ type UnrecognizedEnum = {
6
+ present: false;
7
+ } | {
8
+ present: true;
9
+ name: string;
10
+ };
11
+ export declare function generateEnum(ctx: Context, fullName: string, enumDesc: EnumDescriptorProto, sourceInfo: SourceInfo): Code;
12
+ /** Generates a function with a big switch statement to decode JSON -> our enum. */
13
+ export declare function generateEnumFromJson(ctx: Context, fullName: string, enumDesc: EnumDescriptorProto, unrecognizedEnum: UnrecognizedEnum): Code;
14
+ /** Generates a function with a big switch statement to encode our enum -> JSON. */
15
+ export declare function generateEnumToJson(ctx: Context, fullName: string, enumDesc: EnumDescriptorProto, unrecognizedEnum: UnrecognizedEnum): Code;
16
+ /** Generates a function with a big switch statement to encode our string enum -> int value. */
17
+ export declare function generateEnumToNumber(ctx: Context, fullName: string, enumDesc: EnumDescriptorProto, unrecognizedEnum: UnrecognizedEnum): Code;
18
+ export declare function getMemberName(ctx: Context, enumDesc: EnumDescriptorProto, valueDesc: EnumValueDescriptorProto): string;
19
+ export {};
@@ -0,0 +1,6 @@
1
+ import { Code } from "ts-poet";
2
+ import { Utils } from "./main";
3
+ /** Creates a function to transform a message Source to a Uint8Array Source. */
4
+ export declare function generateEncodeTransform(utils: Utils, fullName: string): Code;
5
+ /** Creates a function to transform a Uint8Array Source to a message Source. */
6
+ export declare function generateDecodeTransform(utils: Utils, fullName: string): Code;
@@ -0,0 +1,8 @@
1
+ import { Code } from "ts-poet";
2
+ import { FileDescriptorProto, ServiceDescriptorProto } from "ts-proto-descriptors";
3
+ import { Context } from "./context";
4
+ import SourceInfo from "./sourceInfo";
5
+ /**
6
+ * Generates a framework-agnostic service descriptor.
7
+ */
8
+ export declare function generateGenericServiceDefinition(ctx: Context, fileDesc: FileDescriptorProto, sourceInfo: SourceInfo, serviceDesc: ServiceDescriptorProto): Code;
@@ -0,0 +1,9 @@
1
+ import { Code } from "ts-poet";
2
+ import { FileDescriptorProto, ServiceDescriptorProto } from "ts-proto-descriptors";
3
+ import { Context } from "./context";
4
+ import SourceInfo from "./sourceInfo";
5
+ /**
6
+ * Generates a service definition and server / client stubs for the
7
+ * `@grpc/grpc-js` library.
8
+ */
9
+ export declare function generateGrpcJsService(ctx: Context, fileDesc: FileDescriptorProto, sourceInfo: SourceInfo, serviceDesc: ServiceDescriptorProto): Code;
@@ -0,0 +1,17 @@
1
+ import { MethodDescriptorProto, FileDescriptorProto, ServiceDescriptorProto } from "ts-proto-descriptors";
2
+ import { Code } from "ts-poet";
3
+ import { Context } from "./context";
4
+ /** Generates a client that uses the `@improbable-web/grpc-web` library. */
5
+ export declare function generateGrpcClientImpl(ctx: Context, _fileDesc: FileDescriptorProto, serviceDesc: ServiceDescriptorProto): Code;
6
+ /** Creates the service descriptor that grpc-web needs at runtime. */
7
+ export declare function generateGrpcServiceDesc(fileDesc: FileDescriptorProto, serviceDesc: ServiceDescriptorProto): Code;
8
+ /**
9
+ * Creates the method descriptor that grpc-web needs at runtime to make `unary` calls.
10
+ *
11
+ * Note that we take a few liberties in the implementation give we don't 100% match
12
+ * what grpc-web's existing output is, but it works out; see comments in the method
13
+ * implementation.
14
+ */
15
+ export declare function generateGrpcMethodDesc(ctx: Context, serviceDesc: ServiceDescriptorProto, methodDesc: MethodDescriptorProto): Code;
16
+ /** Adds misc top-level definitions for grpc-web functionality. */
17
+ export declare function addGrpcWebMisc(ctx: Context, hasStreamingMethods: boolean): Code;
@@ -0,0 +1,7 @@
1
+ import { FileDescriptorProto, ServiceDescriptorProto } from "ts-proto-descriptors";
2
+ import { Code } from "ts-poet";
3
+ import SourceInfo from "./sourceInfo";
4
+ import { Context } from "./context";
5
+ export declare function generateNestjsServiceController(ctx: Context, fileDesc: FileDescriptorProto, sourceInfo: SourceInfo, serviceDesc: ServiceDescriptorProto): Code;
6
+ export declare function generateNestjsServiceClient(ctx: Context, fileDesc: FileDescriptorProto, sourceInfo: SourceInfo, serviceDesc: ServiceDescriptorProto): Code;
7
+ export declare function generateNestjsGrpcServiceMethodsDecorator(ctx: Context, serviceDesc: ServiceDescriptorProto): Code;
@@ -0,0 +1,8 @@
1
+ import { Code } from "ts-poet";
2
+ import { FileDescriptorProto, ServiceDescriptorProto } from "ts-proto-descriptors";
3
+ import { Context } from "./context";
4
+ import SourceInfo from "./sourceInfo";
5
+ /**
6
+ * Generates server / client stubs for `nice-grpc` library.
7
+ */
8
+ export declare function generateNiceGrpcService(ctx: Context, fileDesc: FileDescriptorProto, sourceInfo: SourceInfo, serviceDesc: ServiceDescriptorProto): Code;
@@ -0,0 +1,31 @@
1
+ import { FileDescriptorProto, ServiceDescriptorProto } from "ts-proto-descriptors";
2
+ import { Code } from "ts-poet";
3
+ import SourceInfo from "./sourceInfo";
4
+ import { Context } from "./context";
5
+ /**
6
+ * Generates an interface for `serviceDesc`.
7
+ *
8
+ * Some RPC frameworks (i.e. Twirp) can use the same interface, i.e.
9
+ * `getFoo(req): Promise<res>` for the client-side and server-side,
10
+ * which is the intent for this interface.
11
+ *
12
+ * Other RPC frameworks (i.e. NestJS) that need different client-side
13
+ * vs. server-side code/interfaces are handled separately.
14
+ */
15
+ export declare function generateService(ctx: Context, fileDesc: FileDescriptorProto, sourceInfo: SourceInfo, serviceDesc: ServiceDescriptorProto): Code;
16
+ export declare function generateServiceClientImpl(ctx: Context, fileDesc: FileDescriptorProto, serviceDesc: ServiceDescriptorProto): Code;
17
+ /**
18
+ * Creates an `Rpc.request(service, method, data)` abstraction.
19
+ *
20
+ * This lets clients pass in their own request-promise-ish client.
21
+ *
22
+ * This also requires clientStreamingRequest, serverStreamingRequest and
23
+ * bidirectionalStreamingRequest methods if any of the RPCs is streaming.
24
+ *
25
+ * We don't export this because if a project uses multiple `*.proto` files,
26
+ * we don't want our the barrel imports in `index.ts` to have multiple `Rpc`
27
+ * types.
28
+ */
29
+ export declare function generateRpcType(ctx: Context, hasStreamingMethods: boolean): Code;
30
+ export declare function generateDataLoadersType(): Code;
31
+ export declare function generateDataLoaderOptionsType(): Code;
@@ -0,0 +1,36 @@
1
+ import { Context } from "./context";
2
+ import { Code } from "ts-poet";
3
+ export type StructFieldNames = {
4
+ nullValue: string;
5
+ numberValue: string;
6
+ stringValue: string;
7
+ boolValue: string;
8
+ structValue: string;
9
+ listValue: string;
10
+ };
11
+ /** Whether we need to generate `.wrap` and `.unwrap` methods for the given type. */
12
+ export declare function isWrapperType(fullProtoTypeName: string): boolean;
13
+ /**
14
+ * Converts ts-proto's idiomatic Struct/Value/ListValue representation to the proto messages.
15
+ *
16
+ * We do this deeply b/c NestJS does not invoke wrappers recursively.
17
+ */
18
+ export declare function generateWrapDeep(ctx: Context, fullProtoTypeName: string, fieldNames: StructFieldNames): Code[];
19
+ /**
20
+ * Converts proto's Struct/Value?listValue messages to ts-proto's idiomatic representation.
21
+ *
22
+ * We do this deeply b/c NestJS does not invoke wrappers recursively.
23
+ */
24
+ export declare function generateUnwrapDeep(ctx: Context, fullProtoTypeName: string, fieldNames: StructFieldNames): Code[];
25
+ /**
26
+ * Converts ts-proto's idiomatic Struct/Value/ListValue representation to the proto messages.
27
+ *
28
+ * We do this shallow's b/c ts-proto's encode methods handle the recursion.
29
+ */
30
+ export declare function generateWrapShallow(ctx: Context, fullProtoTypeName: string, fieldNames: StructFieldNames): Code[];
31
+ /**
32
+ * Converts proto's Struct/Value?listValue messages to ts-proto's idiomatic representation.
33
+ *
34
+ * We do this shallowly b/c ts-proto's decode methods handle recursion.
35
+ */
36
+ export declare function generateUnwrapShallow(ctx: Context, fullProtoTypeName: string, fieldNames: StructFieldNames): Code[];
@@ -0,0 +1,3 @@
1
+ import { Code } from "ts-poet";
2
+ import { BaseContext } from "./context";
3
+ export declare function generateTypeRegistry(ctx: BaseContext): Code;
@@ -0,0 +1 @@
1
+ export { DateOption, EnvOption, JsonTimestampOption, LongOption, OneofOption, Options, ServiceOption } from "./options";
package/build/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ServiceOption = exports.OneofOption = exports.LongOption = exports.JsonTimestampOption = exports.EnvOption = exports.DateOption = void 0;
4
+ var options_1 = require("./options");
5
+ Object.defineProperty(exports, "DateOption", { enumerable: true, get: function () { return options_1.DateOption; } });
6
+ Object.defineProperty(exports, "EnvOption", { enumerable: true, get: function () { return options_1.EnvOption; } });
7
+ Object.defineProperty(exports, "JsonTimestampOption", { enumerable: true, get: function () { return options_1.JsonTimestampOption; } });
8
+ Object.defineProperty(exports, "LongOption", { enumerable: true, get: function () { return options_1.LongOption; } });
9
+ Object.defineProperty(exports, "OneofOption", { enumerable: true, get: function () { return options_1.OneofOption; } });
10
+ Object.defineProperty(exports, "ServiceOption", { enumerable: true, get: function () { return options_1.ServiceOption; } });
@@ -0,0 +1,54 @@
1
+ import { Code } from "ts-poet";
2
+ import { ConditionalOutput } from "ts-poet/build/ConditionalOutput";
3
+ import { FileDescriptorProto } from "ts-proto-descriptors";
4
+ import { Context } from "./context";
5
+ import { Options } from "./options";
6
+ export declare function generateFile(ctx: Context, fileDesc: FileDescriptorProto): [string, Code];
7
+ export type Utils = ReturnType<typeof makeDeepPartial> & ReturnType<typeof makeObjectIdMethods> & ReturnType<typeof makeTimestampMethods> & ReturnType<typeof makeByteUtils> & ReturnType<typeof makeLongUtils> & ReturnType<typeof makeComparisonUtils> & ReturnType<typeof makeNiceGrpcServerStreamingMethodResult> & ReturnType<typeof makeGrpcWebErrorClass> & ReturnType<typeof makeExtensionClass> & ReturnType<typeof makeAssertionUtils>;
8
+ /** These are runtime utility methods used by the generated code. */
9
+ export declare function makeUtils(options: Options): Utils;
10
+ declare function makeLongUtils(options: Options, bytes: ReturnType<typeof makeByteUtils>): {
11
+ numberToLong: ConditionalOutput;
12
+ longToNumber: ConditionalOutput;
13
+ longToString: ConditionalOutput;
14
+ longToBigint: ConditionalOutput;
15
+ Long: ConditionalOutput;
16
+ };
17
+ declare function makeByteUtils(options: Options): {
18
+ globalThis: ConditionalOutput;
19
+ bytesFromBase64: ConditionalOutput;
20
+ base64FromBytes: ConditionalOutput;
21
+ };
22
+ declare function makeDeepPartial(options: Options, longs: ReturnType<typeof makeLongUtils>): {
23
+ Builtin: ConditionalOutput;
24
+ DeepPartial: ConditionalOutput;
25
+ Exact: ConditionalOutput;
26
+ };
27
+ declare function makeObjectIdMethods(): {
28
+ fromJsonObjectId: ConditionalOutput;
29
+ fromProtoObjectId: ConditionalOutput;
30
+ toProtoObjectId: ConditionalOutput;
31
+ };
32
+ declare function makeTimestampMethods(options: Options, longs: ReturnType<typeof makeLongUtils>, bytes: ReturnType<typeof makeByteUtils>): {
33
+ toTimestamp: ConditionalOutput;
34
+ fromTimestamp: ConditionalOutput;
35
+ fromJsonTimestamp: ConditionalOutput;
36
+ };
37
+ declare function makeComparisonUtils(): {
38
+ isObject: ConditionalOutput;
39
+ isSet: ConditionalOutput;
40
+ };
41
+ declare function makeNiceGrpcServerStreamingMethodResult(options: Options): {
42
+ NiceGrpcServerStreamingMethodResult: ConditionalOutput;
43
+ };
44
+ declare function makeGrpcWebErrorClass(bytes: ReturnType<typeof makeByteUtils>): {
45
+ GrpcWebError: ConditionalOutput;
46
+ };
47
+ declare function makeExtensionClass(options: Options): {
48
+ Extension: ConditionalOutput;
49
+ };
50
+ declare function makeAssertionUtils(bytes: ReturnType<typeof makeByteUtils>): {
51
+ fail: ConditionalOutput;
52
+ };
53
+ export declare const contextTypeVar = "Context extends DataLoaders";
54
+ export {};
package/build/main.js CHANGED
@@ -715,7 +715,7 @@ function generateOneofProperty(ctx, messageDesc, oneofIndex, sourceInfo) {
715
715
  return (0, ts_poet_1.code) `{ ${mbReadonly}$case: '${fieldName}', ${mbReadonly}${fieldName}: ${typeName} }`;
716
716
  }), { on: " | " });
717
717
  const name = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[oneofIndex].name, options);
718
- return (0, ts_poet_1.code) `${mbReadonly}${name}?: ${unionType} | undefined,`;
718
+ return (0, ts_poet_1.code) `${mbReadonly}${name}?: ${unionType} | ${(0, utils_1.nullOrUndefined)(options)},`;
719
719
  /*
720
720
  // Ideally we'd put the comments for each oneof field next to the anonymous
721
721
  // type we've created in the type union above, but ts-poet currently lacks
@@ -751,7 +751,7 @@ function generateBaseInstanceFactory(ctx, fullName, messageDesc, fullTypeName) {
751
751
  const name = options.useJsonName
752
752
  ? (0, utils_1.getFieldName)(field, options)
753
753
  : (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[oneofIndex].name, ctx.options);
754
- fields.push((0, ts_poet_1.code) `${(0, utils_1.safeAccessor)(name)}: undefined`);
754
+ fields.push((0, ts_poet_1.code) `${(0, utils_1.safeAccessor)(name)}: ${(0, utils_1.nullOrUndefined)(options)}`);
755
755
  }
756
756
  continue;
757
757
  }
@@ -761,7 +761,7 @@ function generateBaseInstanceFactory(ctx, fullName, messageDesc, fullTypeName) {
761
761
  }
762
762
  const fieldKey = (0, utils_1.safeAccessor)((0, utils_1.getFieldName)(field, options));
763
763
  const val = (0, types_1.isWithinOneOf)(field)
764
- ? "undefined"
764
+ ? (0, utils_1.nullOrUndefined)(options)
765
765
  : (0, types_1.isMapType)(ctx, messageDesc, field)
766
766
  ? (0, types_1.shouldGenerateJSMapType)(ctx, messageDesc, field)
767
767
  ? "new Map()"
@@ -910,14 +910,14 @@ function generateDecode(ctx, fullName, messageDesc) {
910
910
  }
911
911
  const initializerSnippet = initializerNecessary
912
912
  ? `
913
- if (${messageProperty} === undefined) {
913
+ if (${messageProperty} === undefined ${(0, utils_1.withOrMaybeCheckIsNull)(options, messageProperty)}) {
914
914
  ${messageProperty} = ${generateMapType ? "new Map()" : "{}"};
915
915
  }`
916
916
  : "";
917
917
  chunks.push((0, ts_poet_1.code) `
918
918
  ${tagCheck}
919
919
  const ${varName} = ${readSnippet};
920
- if (${varName}.value !== undefined) {
920
+ if (${varName}.value !== undefined ${(0, utils_1.withAndMaybeCheckIsNotNull)(options, `${varName}.value`)}) {
921
921
  ${initializerSnippet}
922
922
  ${valueSetterSnippet};
923
923
  }
@@ -926,7 +926,7 @@ function generateDecode(ctx, fullName, messageDesc) {
926
926
  else {
927
927
  const initializerSnippet = initializerNecessary
928
928
  ? `
929
- if (${messageProperty} === undefined) {
929
+ if (${messageProperty} === undefined ${(0, utils_1.withOrMaybeCheckIsNull)(options, messageProperty)}) {
930
930
  ${messageProperty} = [];
931
931
  }`
932
932
  : "";
@@ -992,7 +992,7 @@ function generateDecode(ctx, fullName, messageDesc) {
992
992
  let maybeNonNullAssertion = options.initializeFieldsAsUndefined ? "!" : "";
993
993
  if (!options.initializeFieldsAsUndefined) {
994
994
  unknownFieldsInitializerSnippet = `
995
- if (message._unknownFields === undefined) {
995
+ if (message._unknownFields === undefined ${(0, utils_1.withOrMaybeCheckIsNull)(options, `message._unknownFields`)}) {
996
996
  message._unknownFields = {};
997
997
  }
998
998
  `;
@@ -1005,7 +1005,7 @@ function generateDecode(ctx, fullName, messageDesc) {
1005
1005
  ${unknownFieldsInitializerSnippet}
1006
1006
  const list = message._unknownFields${maybeNonNullAssertion}[tag];
1007
1007
 
1008
- if (list === undefined) {
1008
+ if (list === undefined ${(0, utils_1.withOrMaybeCheckIsNull)(options, `message._unknownFields`)}) {
1009
1009
  message._unknownFields${maybeNonNullAssertion}[tag] = [buf];
1010
1010
  } else {
1011
1011
  list.push(buf);
@@ -1123,7 +1123,7 @@ function generateEncode(ctx, fullName, messageDesc) {
1123
1123
  const maybeTypeField = (0, options_1.addTypeToMessages)(options) ? `$type: '${field.typeName.slice(1)}',` : "";
1124
1124
  const entryWriteSnippet = (0, types_1.isValueType)(ctx, valueType)
1125
1125
  ? (0, ts_poet_1.code) `
1126
- if (value !== undefined) {
1126
+ if (value !== undefined ${(0, utils_1.withOrMaybeCheckIsNotNull)(options, `value`)}) {
1127
1127
  ${writeSnippet(`{ ${maybeTypeField} key: key as any, value }`)};
1128
1128
  }
1129
1129
  `
@@ -1234,7 +1234,7 @@ function generateEncode(ctx, fullName, messageDesc) {
1234
1234
  }
1235
1235
  if (isOptional) {
1236
1236
  chunks.push((0, ts_poet_1.code) `
1237
- if (${messageProperty} !== undefined && ${messageProperty}.length !== 0) {
1237
+ if (${messageProperty} !== undefined ${(0, utils_1.withAndMaybeCheckIsNotNull)(options, messageProperty)} && ${messageProperty}.length !== 0) {
1238
1238
  ${listWriteSnippet}
1239
1239
  }
1240
1240
  `);
@@ -1264,14 +1264,14 @@ function generateEncode(ctx, fullName, messageDesc) {
1264
1264
  else if ((0, types_1.isWithinOneOf)(field)) {
1265
1265
  // Oneofs don't have a default value check b/c they need to denote which-oneof presence
1266
1266
  chunks.push((0, ts_poet_1.code) `
1267
- if (${messageProperty} !== undefined) {
1267
+ if (${messageProperty} !== undefined ${(0, utils_1.withAndMaybeCheckIsNotNull)(options, messageProperty)}) {
1268
1268
  ${writeSnippet(`${messageProperty}`)};
1269
1269
  }
1270
1270
  `);
1271
1271
  }
1272
1272
  else if ((0, types_1.isMessage)(field)) {
1273
1273
  chunks.push((0, ts_poet_1.code) `
1274
- if (${messageProperty} !== undefined) {
1274
+ if (${messageProperty} !== undefined ${(0, utils_1.withAndMaybeCheckIsNotNull)(options, messageProperty)}) {
1275
1275
  ${writeSnippet(`${messageProperty}`)};
1276
1276
  }
1277
1277
  `);
@@ -1697,7 +1697,7 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1697
1697
  const fieldType = (0, types_1.toTypeName)(ctx, messageDesc, field);
1698
1698
  const i = convertFromObjectKey(ctx, messageDesc, field, "key");
1699
1699
  if ((0, types_1.shouldGenerateJSMapType)(ctx, messageDesc, field)) {
1700
- const fallback = noDefaultValue ? "undefined" : "new Map()";
1700
+ const fallback = noDefaultValue ? (0, utils_1.nullOrUndefined)(options) : "new Map()";
1701
1701
  chunks.push((0, ts_poet_1.code) `
1702
1702
  ${fieldKey}: ${ctx.utils.isObject}(${jsonProperty})
1703
1703
  ? Object.entries(${jsonProperty}).reduce<${fieldType}>((acc, [key, value]) => {
@@ -1708,7 +1708,7 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1708
1708
  `);
1709
1709
  }
1710
1710
  else {
1711
- const fallback = noDefaultValue ? "undefined" : "{}";
1711
+ const fallback = noDefaultValue ? (0, utils_1.nullOrUndefined)(options) : "{}";
1712
1712
  chunks.push((0, ts_poet_1.code) `
1713
1713
  ${fieldKey}: ${ctx.utils.isObject}(${jsonProperty})
1714
1714
  ? Object.entries(${jsonProperty}).reduce<${fieldType}>((acc, [key, value]) => {
@@ -1720,7 +1720,7 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1720
1720
  }
1721
1721
  }
1722
1722
  else {
1723
- const fallback = noDefaultValue ? "undefined" : "[]";
1723
+ const fallback = noDefaultValue ? (0, utils_1.nullOrUndefined)(options) : "[]";
1724
1724
  const readValueSnippet = readSnippet("e");
1725
1725
  if (readValueSnippet.toString() === (0, ts_poet_1.code) `e`.toString()) {
1726
1726
  chunks.push((0, ts_poet_1.code) `${fieldKey}: ${ctx.utils.globalThis}.Array.isArray(${jsonPropertyOptional}) ? [...${jsonProperty}] : [],`);
@@ -1745,29 +1745,29 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1745
1745
  const ternaryThen = (0, ts_poet_1.code) `{ $case: '${fieldName}', ${fieldKey}: ${readSnippet(`${jsonProperty}`)}`;
1746
1746
  chunks.push((0, ts_poet_1.code) `${ternaryIf} ? ${ternaryThen}} : `);
1747
1747
  if (field === lastCase) {
1748
- chunks.push((0, ts_poet_1.code) `undefined,`);
1748
+ chunks.push((0, ts_poet_1.code) `${(0, utils_1.nullOrUndefined)(options)},`);
1749
1749
  }
1750
1750
  }
1751
1751
  else if ((0, types_1.isAnyValueType)(field)) {
1752
1752
  chunks.push((0, ts_poet_1.code) `${fieldKey}: ${ctx.utils.isSet}(${jsonPropertyOptional})
1753
1753
  ? ${readSnippet(`${jsonProperty}`)}
1754
- : undefined,
1754
+ : ${(0, utils_1.nullOrUndefined)(options)},
1755
1755
  `);
1756
1756
  }
1757
1757
  else if ((0, types_1.isStructType)(field)) {
1758
1758
  chunks.push((0, ts_poet_1.code) `${fieldKey}: ${ctx.utils.isObject}(${jsonProperty})
1759
1759
  ? ${readSnippet(`${jsonProperty}`)}
1760
- : undefined,`);
1760
+ : ${(0, utils_1.nullOrUndefined)(options)},`);
1761
1761
  }
1762
1762
  else if ((0, types_1.isListValueType)(field)) {
1763
1763
  chunks.push((0, ts_poet_1.code) `
1764
1764
  ${fieldKey}: ${ctx.utils.globalThis}.Array.isArray(${jsonProperty})
1765
1765
  ? ${readSnippet(`${jsonProperty}`)}
1766
- : undefined,
1766
+ : ${(0, utils_1.nullOrUndefined)(options)},
1767
1767
  `);
1768
1768
  }
1769
1769
  else {
1770
- const fallback = (0, types_1.isWithinOneOf)(field) || noDefaultValue ? "undefined" : (0, types_1.defaultValue)(ctx, field);
1770
+ const fallback = (0, types_1.isWithinOneOf)(field) || noDefaultValue ? (0, utils_1.nullOrUndefined)(options) : (0, types_1.defaultValue)(ctx, field);
1771
1771
  chunks.push((0, ts_poet_1.code) `
1772
1772
  ${fieldKey}: ${ctx.utils.isSet}(${jsonProperty})
1773
1773
  ? ${readSnippet(`${jsonProperty}`)}
@@ -1780,9 +1780,9 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1780
1780
  chunks.push((0, ts_poet_1.code) `}`);
1781
1781
  return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
1782
1782
  }
1783
- function generateCanonicalToJson(fullName, fullProtobufTypeName, { useOptionals }) {
1783
+ function generateCanonicalToJson(fullName, fullProtobufTypeName, { useOptionals, useNullAsOptional }) {
1784
1784
  if ((0, types_1.isFieldMaskTypeName)(fullProtobufTypeName)) {
1785
- const returnType = useOptionals === "all" ? "string | undefined" : "string";
1785
+ const returnType = useOptionals === "all" ? `string | ${(0, utils_1.nullOrUndefined)({ useNullAsOptional })}` : "string";
1786
1786
  const pathModifier = useOptionals === "all" ? "?" : "";
1787
1787
  return (0, ts_poet_1.code) `
1788
1788
  toJSON(message: ${fullName}): ${returnType} {
@@ -1954,7 +1954,7 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1954
1954
  let emitDefaultValuesForJson = ctx.options.emitDefaultValues.includes("json-methods");
1955
1955
  const check = ((0, types_1.isScalar)(field) || (0, types_1.isEnum)(field)) && !((0, types_1.isWithinOneOf)(field) || emitDefaultValuesForJson)
1956
1956
  ? (0, types_1.notDefaultCheck)(ctx, field, messageDesc.options, `${messageProperty}`)
1957
- : `${messageProperty} !== undefined`;
1957
+ : `${messageProperty} !== undefined ${(0, utils_1.withAndMaybeCheckIsNotNull)(options, messageProperty)}`;
1958
1958
  chunks.push((0, ts_poet_1.code) `
1959
1959
  if (${check}) {
1960
1960
  ${jsonProperty} = ${readSnippet(`${messageProperty}`)};
@@ -0,0 +1,101 @@
1
+ import { ToStringOpts } from "ts-poet/build/Code";
2
+ export declare enum LongOption {
3
+ NUMBER = "number",
4
+ LONG = "long",
5
+ STRING = "string",
6
+ BIGINT = "bigint"
7
+ }
8
+ export declare enum DateOption {
9
+ DATE = "date",
10
+ STRING = "string",
11
+ STRING_NANO = "string-nano",
12
+ TIMESTAMP = "timestamp"
13
+ }
14
+ export declare enum JsonTimestampOption {
15
+ RFC3339 = "rfc3339",
16
+ RAW = "raw"
17
+ }
18
+ export declare enum EnvOption {
19
+ NODE = "node",
20
+ BROWSER = "browser",
21
+ BOTH = "both"
22
+ }
23
+ export declare enum OneofOption {
24
+ PROPERTIES = "properties",
25
+ UNIONS = "unions"
26
+ }
27
+ export declare enum ServiceOption {
28
+ GRPC = "grpc-js",
29
+ NICE_GRPC = "nice-grpc",
30
+ GENERIC = "generic-definitions",
31
+ DEFAULT = "default",
32
+ NONE = "none"
33
+ }
34
+ export type Options = {
35
+ context: boolean;
36
+ snakeToCamel: Array<"json" | "keys">;
37
+ forceLong: LongOption;
38
+ globalThisPolyfill: boolean;
39
+ useOptionals: boolean | "none" | "deprecatedOnly" | "messages" | "all";
40
+ emitDefaultValues: Array<"json-methods">;
41
+ useDate: DateOption;
42
+ useJsonTimestamp: JsonTimestampOption;
43
+ useMongoObjectId: boolean;
44
+ oneof: OneofOption;
45
+ esModuleInterop: boolean;
46
+ fileSuffix: string;
47
+ importSuffix: string;
48
+ outputEncodeMethods: true | false | "encode-only" | "decode-only" | "encode-no-creation";
49
+ outputJsonMethods: true | false | "to-only" | "from-only";
50
+ outputPartialMethods: boolean;
51
+ outputTypeAnnotations: boolean | "static-only" | "optional";
52
+ outputTypeRegistry: boolean;
53
+ stringEnums: boolean;
54
+ constEnums: boolean;
55
+ removeEnumPrefix: boolean;
56
+ enumsAsLiterals: boolean;
57
+ outputClientImpl: boolean | "grpc-web";
58
+ outputServices: ServiceOption[];
59
+ addGrpcMetadata: boolean;
60
+ metadataType: string | undefined;
61
+ addNestjsRestParameter: boolean;
62
+ returnObservable: boolean;
63
+ lowerCaseServiceMethods: boolean;
64
+ nestJs: boolean;
65
+ env: EnvOption;
66
+ unrecognizedEnum: boolean;
67
+ unrecognizedEnumName: string;
68
+ unrecognizedEnumValue: number;
69
+ exportCommonSymbols: boolean;
70
+ outputSchema: boolean;
71
+ onlyTypes: boolean;
72
+ emitImportedFiles: boolean;
73
+ useAbortSignal: boolean;
74
+ useExactTypes: boolean;
75
+ useAsyncIterable: boolean;
76
+ unknownFields: boolean;
77
+ usePrototypeForDefaults: boolean;
78
+ useJsonName: boolean;
79
+ useJsonWireFormat: boolean;
80
+ useNumericEnumForJson: boolean;
81
+ initializeFieldsAsUndefined: boolean;
82
+ useMapType: boolean;
83
+ useReadonlyTypes: boolean;
84
+ useSnakeTypeName: boolean;
85
+ outputExtensions: boolean;
86
+ outputIndex: boolean;
87
+ M: {
88
+ [from: string]: string;
89
+ };
90
+ rpcBeforeRequest: boolean;
91
+ rpcAfterResponse: boolean;
92
+ rpcErrorHandler: boolean;
93
+ comments: boolean;
94
+ disableProto2Optionals: boolean;
95
+ disableProto2DefaultValues: boolean;
96
+ useNullAsOptional: boolean;
97
+ };
98
+ export declare function defaultOptions(): Options;
99
+ export declare function optionsFromParameter(parameter: string | undefined): Options;
100
+ export declare function getTsPoetOpts(options: Options): ToStringOpts;
101
+ export declare function addTypeToMessages(options: Options): boolean;
package/build/options.js CHANGED
@@ -100,6 +100,7 @@ function defaultOptions() {
100
100
  comments: true,
101
101
  disableProto2Optionals: false,
102
102
  disableProto2DefaultValues: false,
103
+ useNullAsOptional: false,
103
104
  };
104
105
  }
105
106
  exports.defaultOptions = defaultOptions;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { FileDescriptorProto } from "ts-proto-descriptors";
2
+ import { Code } from "ts-poet";
3
+ import { Context } from "./context";
4
+ import SourceInfo from "./sourceInfo";
5
+ export declare function generateSchema(ctx: Context, fileDesc: FileDescriptorProto, sourceInfo: SourceInfo): Code[];
@@ -0,0 +1,78 @@
1
+ import { FileDescriptorProto } from "ts-proto-descriptors";
2
+ /** This type is expecting a value from the Fields constant. */
3
+ export type FieldID = number;
4
+ /**
5
+ * The field values here represent the proto field IDs associated with the types
6
+ * (file,message,enum,service).
7
+ *
8
+ * For more information read the comments for SourceCodeInfo declared in
9
+ * google's 'descriptor.proto' file, see:
10
+ * https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/descriptor.proto#L730
11
+ */
12
+ export declare const Fields: {
13
+ file: {
14
+ syntax: number;
15
+ message_type: number;
16
+ enum_type: number;
17
+ service: number;
18
+ extension: number;
19
+ };
20
+ message: {
21
+ field: number;
22
+ nested_type: number;
23
+ enum_type: number;
24
+ oneof_decl: number;
25
+ };
26
+ enum: {
27
+ value: number;
28
+ };
29
+ service: {
30
+ method: number;
31
+ };
32
+ };
33
+ /**
34
+ * This type is simply an interface on the SourceCodeInfo.Location message.
35
+ */
36
+ export interface SourceDescription {
37
+ readonly span: number[];
38
+ readonly leadingComments: string;
39
+ readonly trailingComments: string;
40
+ readonly leadingDetachedComments: string[];
41
+ }
42
+ /**
43
+ * Mapping from a string of dotted notation `path` parts to efficiently
44
+ * lookup the related source information.
45
+ */
46
+ export type SourceInfoMap = {
47
+ [key: string]: SourceDescription;
48
+ };
49
+ /**
50
+ * This class provides direct lookup and navigation through the type
51
+ * system by the use of lookup/open to access the source info for types
52
+ * defined in a protocol buffer.
53
+ */
54
+ export default class SourceInfo implements SourceDescription {
55
+ private readonly sourceCode;
56
+ private readonly selfDescription;
57
+ /** Returns an empty SourceInfo */
58
+ static empty(): SourceInfo;
59
+ /**
60
+ * Creates the SourceInfo from the FileDescriptorProto given to you
61
+ * by the protoc compiler. It indexes file.sourceCodeInfo by dotted
62
+ * path notation and returns the root SourceInfo.
63
+ */
64
+ static fromDescriptor(file: FileDescriptorProto): SourceInfo;
65
+ private constructor();
66
+ /** Returns the code span [start line, start column, end line] */
67
+ get span(): number[];
68
+ /** Leading consecutive comment lines prior to the current element */
69
+ get leadingComments(): string;
70
+ /** Documentation is unclear about what exactly this is */
71
+ get trailingComments(): string;
72
+ /** Detached comments are those preceeding but separated by a blank non-comment line */
73
+ get leadingDetachedComments(): string[];
74
+ /** Return the source info for the field id and index specficied */
75
+ lookup(type: FieldID, index?: number): SourceDescription;
76
+ /** Returns a new SourceInfo class representing the field id and index specficied */
77
+ open(type: FieldID, index: number): SourceInfo;
78
+ }
@@ -0,0 +1,103 @@
1
+ import { CodeGeneratorRequest, DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, FieldDescriptorProto_Type, FileDescriptorProto, MessageOptions, MethodDescriptorProto, ServiceDescriptorProto } from "ts-proto-descriptors";
2
+ import { Code, Import } from "ts-poet";
3
+ import { Options } from "./options";
4
+ import { Context } from "./context";
5
+ /** Based on https://github.com/dcodeIO/protobuf.js/blob/master/src/types.js#L37. */
6
+ export declare function basicWireType(type: FieldDescriptorProto_Type): number;
7
+ export declare function basicLongWireType(type: FieldDescriptorProto_Type): number | undefined;
8
+ /** Returns the type name without any repeated/required/etc. labels. */
9
+ export declare function basicTypeName(ctx: Context, field: FieldDescriptorProto, typeOptions?: {
10
+ keepValueType?: boolean;
11
+ }): Code;
12
+ /** Returns the Reader method for the primitive's read/write call. */
13
+ export declare function toReaderCall(field: FieldDescriptorProto): string;
14
+ export declare function packedType(type: FieldDescriptorProto_Type): number | undefined;
15
+ export declare function defaultValue(ctx: Context, field: FieldDescriptorProto): any;
16
+ /** Creates code that checks that the field is not the default value. Supports scalars and enums. */
17
+ export declare function notDefaultCheck(ctx: Context, field: FieldDescriptorProto, messageOptions: MessageOptions | undefined, place: string): Code;
18
+ /** A map of proto type name, e.g. `foo.Message.Inner`, to module/class name, e.g. `foo`, `Message_Inner`. */
19
+ export type TypeMap = Map<string, [string, string, DescriptorProto | EnumDescriptorProto]>;
20
+ /** Scans all of the proto files in `request` and builds a map of proto typeName -> TS module/name. */
21
+ export declare function createTypeMap(request: CodeGeneratorRequest, options: Options): TypeMap;
22
+ /** A "Scalar Value Type" as defined in https://developers.google.com/protocol-buffers/docs/proto3#scalar */
23
+ export declare function isScalar(field: FieldDescriptorProto): boolean;
24
+ export declare function isOptionalProperty(field: FieldDescriptorProto, messageOptions: MessageOptions | undefined, options: Options, isProto3Syntax: boolean): boolean;
25
+ /** This includes all scalars, enums and the [groups type](https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/DescriptorProtos.FieldDescriptorProto.Type.html#TYPE_GROUP) */
26
+ export declare function isPrimitive(field: FieldDescriptorProto): boolean;
27
+ export declare function isBytes(field: FieldDescriptorProto): boolean;
28
+ export declare function isMessage(field: FieldDescriptorProto): boolean;
29
+ export declare function isEnum(field: FieldDescriptorProto): boolean;
30
+ export declare function isWithinOneOf(field: FieldDescriptorProto): boolean;
31
+ export declare function isWithinOneOfThatShouldBeUnion(options: Options, field: FieldDescriptorProto): boolean;
32
+ export declare function isRepeated(field: FieldDescriptorProto): boolean;
33
+ export declare function isLong(field: FieldDescriptorProto): boolean;
34
+ export declare function isWholeNumber(field: FieldDescriptorProto): boolean;
35
+ export declare function isMapType(ctx: Context, messageDesc: DescriptorProto, field: FieldDescriptorProto): boolean;
36
+ export declare function isObjectId(field: FieldDescriptorProto): boolean;
37
+ export declare function isTimestamp(field: FieldDescriptorProto): boolean;
38
+ export declare function isValueType(ctx: Context, field: FieldDescriptorProto): boolean;
39
+ export declare function isAnyValueType(field: FieldDescriptorProto): boolean;
40
+ export declare function isAnyValueTypeName(typeName: string): boolean;
41
+ export declare function isBytesValueType(field: FieldDescriptorProto): boolean;
42
+ export declare function isFieldMaskType(field: FieldDescriptorProto): boolean;
43
+ export declare function isFieldMaskTypeName(typeName: string): boolean;
44
+ export declare function isListValueType(field: FieldDescriptorProto): boolean;
45
+ export declare function isListValueTypeName(typeName: string): boolean;
46
+ export declare function isStructType(field: FieldDescriptorProto): boolean;
47
+ export declare function isStructTypeName(typeName: string): boolean;
48
+ export declare function isLongValueType(field: FieldDescriptorProto): boolean;
49
+ export declare function isEmptyType(typeName: string): boolean;
50
+ export declare function valueTypeName(ctx: Context, typeName: string): Code | undefined;
51
+ export declare function wrapperTypeName(typeName: string): string | undefined;
52
+ /** Maps `.some_proto_namespace.Message` to a TypeName. */
53
+ export declare function messageToTypeName(ctx: Context, protoType: string, typeOptions?: {
54
+ keepValueType?: boolean;
55
+ repeated?: boolean;
56
+ }): Code;
57
+ export declare function getEnumMethod(ctx: Context, enumProtoType: string, methodSuffix: string): Import;
58
+ /** Return the TypeName for any field (primitive/message/etc.) as exposed in the interface. */
59
+ export declare function toTypeName(ctx: Context, messageDesc: DescriptorProto | undefined, field: FieldDescriptorProto, ensureOptional?: boolean): Code;
60
+ /**
61
+ * For a protobuf map field, if the generated code should use the javascript Map type.
62
+ *
63
+ * If the type of a protobuf map key corresponds to the Long type, we always use the Map type. This avoids generating
64
+ * invalid code such as below (using Long as key of a javascript object):
65
+ *
66
+ * export interface Foo {
67
+ * bar: { [key: Long]: Long }
68
+ * }
69
+ *
70
+ * See https://github.com/stephenh/ts-proto/issues/708 for more details.
71
+ */
72
+ export declare function shouldGenerateJSMapType(ctx: Context, message: DescriptorProto, field: FieldDescriptorProto): boolean;
73
+ export declare function detectMapType(ctx: Context, messageDesc: DescriptorProto, fieldDesc: FieldDescriptorProto): {
74
+ messageDesc: DescriptorProto;
75
+ keyField: FieldDescriptorProto;
76
+ keyType: Code;
77
+ valueField: FieldDescriptorProto;
78
+ valueType: Code;
79
+ } | undefined;
80
+ export declare function rawRequestType(ctx: Context, methodDesc: MethodDescriptorProto, typeOptions?: {
81
+ keepValueType?: boolean;
82
+ repeated?: boolean;
83
+ }): Code;
84
+ export declare function observableType(ctx: Context, asType?: boolean): Code;
85
+ export declare function requestType(ctx: Context, methodDesc: MethodDescriptorProto, partial?: boolean): Code;
86
+ export declare function responseType(ctx: Context, methodDesc: MethodDescriptorProto, typeOptions?: {
87
+ keepValueType?: boolean;
88
+ repeated?: boolean;
89
+ }): Code;
90
+ export declare function responsePromise(ctx: Context, methodDesc: MethodDescriptorProto): Code;
91
+ export declare function responseObservable(ctx: Context, methodDesc: MethodDescriptorProto): Code;
92
+ export declare function responsePromiseOrObservable(ctx: Context, methodDesc: MethodDescriptorProto): Code;
93
+ export interface BatchMethod {
94
+ methodDesc: MethodDescriptorProto;
95
+ uniqueIdentifier: string;
96
+ singleMethodName: string;
97
+ inputFieldName: string;
98
+ inputType: Code;
99
+ outputFieldName: string;
100
+ outputType: Code;
101
+ mapType: boolean;
102
+ }
103
+ export declare function detectBatchMethod(ctx: Context, fileDesc: FileDescriptorProto, serviceDesc: ServiceDescriptorProto, methodDesc: MethodDescriptorProto): BatchMethod | undefined;
package/build/types.js CHANGED
@@ -235,7 +235,7 @@ function defaultValue(ctx, field) {
235
235
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_MESSAGE:
236
236
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_GROUP:
237
237
  default:
238
- return "undefined";
238
+ return (0, utils_1.nullOrUndefined)(options);
239
239
  }
240
240
  }
241
241
  exports.defaultValue = defaultValue;
@@ -243,7 +243,9 @@ exports.defaultValue = defaultValue;
243
243
  function notDefaultCheck(ctx, field, messageOptions, place) {
244
244
  const { typeMap, options, currentFile } = ctx;
245
245
  const isOptional = isOptionalProperty(field, messageOptions, options, currentFile.isProto3Syntax);
246
- const maybeNotUndefinedAnd = isOptional ? `${place} !== undefined && ` : "";
246
+ const maybeNotUndefinedAnd = isOptional
247
+ ? `${place} !== undefined ${(0, utils_1.withAndMaybeCheckIsNotNull)(options, place)} &&`
248
+ : "";
247
249
  switch (field.type) {
248
250
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_DOUBLE:
249
251
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_FLOAT:
@@ -552,7 +554,7 @@ function messageToTypeName(ctx, protoType, typeOptions = {}) {
552
554
  if ((_a = typeOptions.repeated) !== null && _a !== void 0 ? _a : false) {
553
555
  return valueType;
554
556
  }
555
- return (0, ts_poet_1.code) `${valueType} | undefined`;
557
+ return (0, ts_poet_1.code) `${valueType} | ${(0, utils_1.nullOrUndefined)(options)}`;
556
558
  }
557
559
  // Look for other special prototypes like Timestamp that aren't technically wrapper types
558
560
  if (!typeOptions.keepValueType && protoType === ".google.protobuf.Timestamp") {
@@ -584,7 +586,7 @@ exports.getEnumMethod = getEnumMethod;
584
586
  function toTypeName(ctx, messageDesc, field, ensureOptional = false) {
585
587
  function finalize(type, isOptional) {
586
588
  if (isOptional) {
587
- return (0, ts_poet_1.code) `${type} | undefined`;
589
+ return (0, ts_poet_1.code) `${type} | ${(0, utils_1.nullOrUndefined)(ctx.options, field.proto3Optional)}`;
588
590
  }
589
591
  return type;
590
592
  }
@@ -0,0 +1,80 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { Code, Import } from "ts-poet";
4
+ import { CodeGeneratorRequest, FieldDescriptorProto, FileDescriptorProto, MethodDescriptorProto, MethodOptions } from "ts-proto-descriptors";
5
+ import ReadStream = NodeJS.ReadStream;
6
+ import { SourceDescription } from "./sourceInfo";
7
+ import { Options } from "./options";
8
+ export declare function protoFilesToGenerate(request: CodeGeneratorRequest): FileDescriptorProto[];
9
+ export declare function generateIndexFiles(files: FileDescriptorProto[], options: Options): [string, Code][];
10
+ export declare function readToBuffer(stream: ReadStream): Promise<Buffer>;
11
+ export declare function fail(message: string): never;
12
+ export declare function singular(name: string): string;
13
+ export declare function lowerFirst(name: string): string;
14
+ export declare function upperFirst(name: string): string;
15
+ /** Removes potentially harmful characters from comments and pushes it into chunks. */
16
+ export declare function maybeAddComment(options: Pick<Options, "comments">, desc: Partial<Pick<SourceDescription, "leadingComments" | "trailingComments">>, chunks: Code[], deprecated?: boolean, prefix?: string): void;
17
+ export declare function prefixDisableLinter(spec: string): string;
18
+ export declare function maybePrefixPackage(fileDesc: FileDescriptorProto, rest: string): string;
19
+ /**
20
+ * Asserts that an object is an instance of a certain class
21
+ * @param obj The object to check
22
+ * @param constructor The constructor of the class to check
23
+ */
24
+ export declare function assertInstanceOf<T>(obj: unknown, constructor: {
25
+ new (...args: any[]): T;
26
+ }): asserts obj is T;
27
+ /**
28
+ * A MethodDescriptorProto subclass that adds formatted properties
29
+ */
30
+ export declare class FormattedMethodDescriptor implements MethodDescriptorProto {
31
+ name: string;
32
+ inputType: string;
33
+ outputType: string;
34
+ options: MethodOptions | undefined;
35
+ clientStreaming: boolean;
36
+ serverStreaming: boolean;
37
+ private original;
38
+ private ctxOptions;
39
+ /**
40
+ * The name of this method with formatting applied according to the `Options` object passed to the constructor.
41
+ * Automatically updates to any changes to the `Options` or `name` of this object
42
+ */
43
+ get formattedName(): string;
44
+ constructor(src: MethodDescriptorProto, options: Options);
45
+ /**
46
+ * Retrieve the source `MethodDescriptorProto` used to construct this object
47
+ * @returns The source `MethodDescriptorProto` used to construct this object
48
+ */
49
+ getSource(): MethodDescriptorProto;
50
+ /**
51
+ * Applies formatting rules to a gRPC method name.
52
+ * @param methodName The original method name
53
+ * @param options The options object containing rules to apply
54
+ * @returns The formatted method name
55
+ */
56
+ static formatName(methodName: string, options: Options): string;
57
+ }
58
+ export declare function getFieldJsonName(field: Pick<FieldDescriptorProto, "name" | "jsonName">, options: Pick<Options, "snakeToCamel" | "useJsonName">): string;
59
+ export declare function getFieldName(field: Pick<FieldDescriptorProto, "name" | "jsonName">, options: Pick<Options, "snakeToCamel" | "useJsonName">): string;
60
+ /** Returns `bar` or `"bar"` if `propertyName` isn't a safe property name. */
61
+ export declare function safeAccessor(propertyName: string): string;
62
+ /**
63
+ * Returns a snippet for reading an object's property, such as `foo.bar`, or `foo['bar']` if the property name contains unusual characters.
64
+ * For simplicity, we don't match the ECMA 5/6 rules for valid identifiers exactly, and return array syntax liberally.
65
+ * @param objectName
66
+ * @param propertyName
67
+ * @param optional
68
+ */
69
+ export declare function getPropertyAccessor(objectName: string, propertyName: string, optional?: boolean): string;
70
+ export declare function impFile(options: Options, spec: string): Import;
71
+ export declare function impProto(options: Options, module: string, type: string): Import;
72
+ export declare function tryCatchBlock(tryBlock: Code | string, handleErrorBlock: Code | string): Code;
73
+ export declare function arrowFunction(params: string, body: Code | string, isOneLine?: boolean): Code;
74
+ export declare function nullOrUndefined(options: Pick<Options, "useNullAsOptional">, hasProto3Optional?: boolean): string;
75
+ export declare function maybeCheckIsNotNull(options: Pick<Options, "useNullAsOptional">, typeName: string, prefix?: string): string;
76
+ export declare function maybeCheckIsNull(options: Pick<Options, "useNullAsOptional">, typeName: string, prefix?: string): string;
77
+ export declare function withOrMaybeCheckIsNotNull(options: Pick<Options, "useNullAsOptional">, typeName: string): string;
78
+ export declare function withOrMaybeCheckIsNull(options: Pick<Options, "useNullAsOptional">, typeName: string): string;
79
+ export declare function withAndMaybeCheckIsNotNull(options: Pick<Options, "useNullAsOptional">, typeName: string): string;
80
+ export declare function withAndMaybeCheckIsNull(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.arrowFunction = exports.tryCatchBlock = exports.impProto = exports.impFile = exports.getPropertyAccessor = exports.safeAccessor = exports.getFieldName = exports.getFieldJsonName = exports.FormattedMethodDescriptor = exports.assertInstanceOf = exports.maybePrefixPackage = exports.prefixDisableLinter = exports.maybeAddComment = exports.upperFirst = exports.lowerFirst = exports.singular = exports.fail = exports.readToBuffer = exports.generateIndexFiles = exports.protoFilesToGenerate = void 0;
3
+ 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.prefixDisableLinter = 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");
@@ -261,3 +261,31 @@ function arrowFunction(params, body, isOneLine = true) {
261
261
  return (0, ts_poet_1.code) `(${params}) => { ${body} }`;
262
262
  }
263
263
  exports.arrowFunction = arrowFunction;
264
+ function nullOrUndefined(options, hasProto3Optional = false) {
265
+ return options.useNullAsOptional ? `null ${hasProto3Optional ? "| undefined" : ""}` : "undefined";
266
+ }
267
+ exports.nullOrUndefined = nullOrUndefined;
268
+ function maybeCheckIsNotNull(options, typeName, prefix) {
269
+ return options.useNullAsOptional ? ` ${prefix} ${typeName} !== null` : "";
270
+ }
271
+ exports.maybeCheckIsNotNull = maybeCheckIsNotNull;
272
+ function maybeCheckIsNull(options, typeName, prefix) {
273
+ return options.useNullAsOptional ? ` ${prefix} ${typeName} === null` : "";
274
+ }
275
+ exports.maybeCheckIsNull = maybeCheckIsNull;
276
+ function withOrMaybeCheckIsNotNull(options, typeName) {
277
+ return maybeCheckIsNotNull(options, typeName, "||");
278
+ }
279
+ exports.withOrMaybeCheckIsNotNull = withOrMaybeCheckIsNotNull;
280
+ function withOrMaybeCheckIsNull(options, typeName) {
281
+ return maybeCheckIsNull(options, typeName, "||");
282
+ }
283
+ exports.withOrMaybeCheckIsNull = withOrMaybeCheckIsNull;
284
+ function withAndMaybeCheckIsNotNull(options, typeName) {
285
+ return maybeCheckIsNotNull(options, typeName, "&&");
286
+ }
287
+ exports.withAndMaybeCheckIsNotNull = withAndMaybeCheckIsNotNull;
288
+ function withAndMaybeCheckIsNull(options, typeName) {
289
+ return maybeCheckIsNotNull(options, typeName, "&&");
290
+ }
291
+ exports.withAndMaybeCheckIsNull = withAndMaybeCheckIsNull;
@@ -0,0 +1,8 @@
1
+ import { DescriptorProto, EnumDescriptorProto, FileDescriptorProto, ServiceDescriptorProto } from "ts-proto-descriptors";
2
+ import SourceInfo from "./sourceInfo";
3
+ import { Options } from "./options";
4
+ type MessageVisitor = (fullName: string, desc: DescriptorProto, sourceInfo: SourceInfo, fullProtoTypeName: string) => void;
5
+ type EnumVisitor = (fullName: string, desc: EnumDescriptorProto, sourceInfo: SourceInfo, fullProtoTypeName: string) => void;
6
+ export declare function visit(proto: FileDescriptorProto | DescriptorProto, sourceInfo: SourceInfo, messageFn: MessageVisitor, options: Options, enumFn?: EnumVisitor, tsPrefix?: string, protoPrefix?: string): void;
7
+ export declare function visitServices(proto: FileDescriptorProto, sourceInfo: SourceInfo, serviceFn: (desc: ServiceDescriptorProto, sourceInfo: SourceInfo) => void): void;
8
+ export {};
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.170.0",
3
+ "version": "1.172.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",
7
7
  "bin": {
8
8
  "protoc-gen-ts_proto": "./protoc-gen-ts_proto"
9
9
  },
10
+ "types": "build/index.d.ts",
10
11
  "scripts": {
11
12
  "build": "yarn tsc",
12
13
  "build:test": "yarn proto2bin && yarn proto2pbjs && yarn bin2ts",