From f143222ba309f3f12dccdda0253b3f9a65a57eae Mon Sep 17 00:00:00 2001 From: ilikdoge Date: Sun, 26 Mar 2023 17:35:23 -0700 Subject: [PATCH 1/3] feat: fromPartial do not default initialize --- .../from-partial-no-initialize/parameters.txt | 1 + .../partials-test.ts | 50 +++ .../from-partial-no-initialize/test.bin | Bin 0 -> 1027 bytes .../from-partial-no-initialize/test.proto | 15 + .../from-partial-no-initialize/test.ts | 372 ++++++++++++++++++ .../use-optionals-no-undefined/test.ts | 35 +- src/main.ts | 22 +- 7 files changed, 472 insertions(+), 23 deletions(-) create mode 100644 integration/from-partial-no-initialize/parameters.txt create mode 100644 integration/from-partial-no-initialize/partials-test.ts create mode 100644 integration/from-partial-no-initialize/test.bin create mode 100644 integration/from-partial-no-initialize/test.proto create mode 100644 integration/from-partial-no-initialize/test.ts diff --git a/integration/from-partial-no-initialize/parameters.txt b/integration/from-partial-no-initialize/parameters.txt new file mode 100644 index 000000000..48ec4ec23 --- /dev/null +++ b/integration/from-partial-no-initialize/parameters.txt @@ -0,0 +1 @@ +useOptionals=all,initializeFieldsAsUndefined=false \ No newline at end of file diff --git a/integration/from-partial-no-initialize/partials-test.ts b/integration/from-partial-no-initialize/partials-test.ts new file mode 100644 index 000000000..2094a032e --- /dev/null +++ b/integration/from-partial-no-initialize/partials-test.ts @@ -0,0 +1,50 @@ +import { TPartial } from './test' + +describe('partials-test', () => { + it('works with singular fields', () => { + const test = { + number: 2, + string: 'hi', + message: { + field: 'hello' + } + }; + + const result = TPartial.fromPartial(test); + + expect(result).toEqual(test); + }); + + it('works with repeated fields', () => { + const test = { + map: { + a: 'b', + c: 'd' + }, + + repeatedMessage: [ + { + field: 'world' + }, + { + field: 'hello' + } + ], + + repeatedString: [ + 'goodbye', + 'world' + ], + + repeatedNumber: [ + 1, + 2, + 3 + ] + }; + + const result = TPartial.fromPartial(test); + + expect(result).toEqual(test); + }); +}) diff --git a/integration/from-partial-no-initialize/test.bin b/integration/from-partial-no-initialize/test.bin new file mode 100644 index 0000000000000000000000000000000000000000..039b07516c1cb1005c21de95659e16aeb69ab76e GIT binary patch literal 1027 zcmZXR&2G~`6ooxMw#V0Y>kQDEBt;=4Z3I$4T_6N&Hb_Otu&<12s)!_w92cp)CR?6> zr{Ih|V=A%pnS0K6e@xU=`)0YTZ z(htf1wyM@wIh*iBRCHqci<5FreMG_bcJsBW;b9BEu*$sen|igq&N<$r!YVx@u_<@C zn21Guu9uFpyeN0?w@rOln6iFO;ij7*mx#ybS?{#770#gFQC3&Gs%)yOOBeE6{Qsdt z=X4ulY4>z#=M9jJiUarjx!pqFH`;|m82x|7kL?+TuSvOGbVlNPb=O`dD$J+3=#O%J zTiL60;w^u}KKzyxXz6(ej1;j%3(wO;#HFVt_awu&kRH>5t7D2uN{@S52tKBSd?Tb$ z5X(J#q@M=;*Oih(+TQ8-pNz zW=jISsqZZSp?K(OfY4~_l*!FcEd;Tczp^FSs_w*$OF-ftA_0j9g5iq$Z8X literal 0 HcmV?d00001 diff --git a/integration/from-partial-no-initialize/test.proto b/integration/from-partial-no-initialize/test.proto new file mode 100644 index 000000000..a0ea92368 --- /dev/null +++ b/integration/from-partial-no-initialize/test.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +message TPartialMessage{ + required string field = 1; +} + +message TPartial{ + optional int32 number = 1; + optional string string = 2; + map map = 3; + optional TPartialMessage message = 4; + repeated TPartialMessage repeated_message = 5; + repeated string repeated_string = 6; + repeated int32 repeated_number = 7; +} \ No newline at end of file diff --git a/integration/from-partial-no-initialize/test.ts b/integration/from-partial-no-initialize/test.ts new file mode 100644 index 000000000..0f3b545ac --- /dev/null +++ b/integration/from-partial-no-initialize/test.ts @@ -0,0 +1,372 @@ +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; + +export const protobufPackage = ""; + +export interface TPartialMessage { + field?: string; +} + +export interface TPartial { + number?: number; + string?: string; + map?: { [key: string]: string }; + message?: TPartialMessage; + repeatedMessage?: TPartialMessage[]; + repeatedString?: string[]; + repeatedNumber?: number[]; +} + +export interface TPartial_MapEntry { + key: string; + value: string; +} + +function createBaseTPartialMessage(): TPartialMessage { + return {}; +} + +export const TPartialMessage = { + encode(message: TPartialMessage, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.field !== undefined && message.field !== "") { + writer.uint32(10).string(message.field); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TPartialMessage { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTPartialMessage(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag != 10) { + break; + } + + message.field = reader.string(); + continue; + } + if ((tag & 7) == 4 || tag == 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TPartialMessage { + return { field: isSet(object.field) ? String(object.field) : "" }; + }, + + toJSON(message: TPartialMessage): unknown { + const obj: any = {}; + message.field !== undefined && (obj.field = message.field); + return obj; + }, + + create, I>>(base?: I): TPartialMessage { + return TPartialMessage.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): TPartialMessage { + const message = createBaseTPartialMessage(); + message.field = object.field ?? undefined; + return message; + }, +}; + +function createBaseTPartial(): TPartial { + return {}; +} + +export const TPartial = { + encode(message: TPartial, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.number !== undefined && message.number !== 0) { + writer.uint32(8).int32(message.number); + } + if (message.string !== undefined && message.string !== "") { + writer.uint32(18).string(message.string); + } + Object.entries(message.map || {}).forEach(([key, value]) => { + TPartial_MapEntry.encode({ key: key as any, value }, writer.uint32(26).fork()).ldelim(); + }); + if (message.message !== undefined) { + TPartialMessage.encode(message.message, writer.uint32(34).fork()).ldelim(); + } + if (message.repeatedMessage !== undefined && message.repeatedMessage.length !== 0) { + for (const v of message.repeatedMessage) { + TPartialMessage.encode(v!, writer.uint32(42).fork()).ldelim(); + } + } + if (message.repeatedString !== undefined && message.repeatedString.length !== 0) { + for (const v of message.repeatedString) { + writer.uint32(50).string(v!); + } + } + if (message.repeatedNumber !== undefined && message.repeatedNumber.length !== 0) { + writer.uint32(58).fork(); + for (const v of message.repeatedNumber) { + writer.int32(v); + } + writer.ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TPartial { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTPartial(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag != 8) { + break; + } + + message.number = reader.int32(); + continue; + case 2: + if (tag != 18) { + break; + } + + message.string = reader.string(); + continue; + case 3: + if (tag != 26) { + break; + } + + const entry3 = TPartial_MapEntry.decode(reader, reader.uint32()); + if (entry3.value !== undefined) { + if (message.map === undefined) { + message.map = {}; + } + + message.map![entry3.key] = entry3.value; + } + continue; + case 4: + if (tag != 34) { + break; + } + + message.message = TPartialMessage.decode(reader, reader.uint32()); + continue; + case 5: + if (tag != 42) { + break; + } + + if (message.repeatedMessage === undefined) { + message.repeatedMessage = []; + } + message.repeatedMessage!.push(TPartialMessage.decode(reader, reader.uint32())); + continue; + case 6: + if (tag != 50) { + break; + } + + if (message.repeatedString === undefined) { + message.repeatedString = []; + } + message.repeatedString!.push(reader.string()); + continue; + case 7: + if (tag == 56) { + if (message.repeatedNumber === undefined) { + message.repeatedNumber = []; + } + message.repeatedNumber!.push(reader.int32()); + continue; + } + + if (tag == 58) { + if (message.repeatedNumber === undefined) { + message.repeatedNumber = []; + } + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.repeatedNumber!.push(reader.int32()); + } + + continue; + } + + break; + } + if ((tag & 7) == 4 || tag == 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TPartial { + return { + number: isSet(object.number) ? Number(object.number) : 0, + string: isSet(object.string) ? String(object.string) : "", + map: isObject(object.map) + ? Object.entries(object.map).reduce<{ [key: string]: string }>((acc, [key, value]) => { + acc[key] = String(value); + return acc; + }, {}) + : {}, + message: isSet(object.message) ? TPartialMessage.fromJSON(object.message) : undefined, + repeatedMessage: Array.isArray(object?.repeatedMessage) + ? object.repeatedMessage.map((e: any) => TPartialMessage.fromJSON(e)) + : [], + repeatedString: Array.isArray(object?.repeatedString) ? object.repeatedString.map((e: any) => String(e)) : [], + repeatedNumber: Array.isArray(object?.repeatedNumber) ? object.repeatedNumber.map((e: any) => Number(e)) : [], + }; + }, + + toJSON(message: TPartial): unknown { + const obj: any = {}; + message.number !== undefined && (obj.number = Math.round(message.number)); + message.string !== undefined && (obj.string = message.string); + obj.map = {}; + if (message.map) { + Object.entries(message.map).forEach(([k, v]) => { + obj.map[k] = v; + }); + } + message.message !== undefined && + (obj.message = message.message ? TPartialMessage.toJSON(message.message) : undefined); + if (message.repeatedMessage) { + obj.repeatedMessage = message.repeatedMessage.map((e) => e ? TPartialMessage.toJSON(e) : undefined); + } else { + obj.repeatedMessage = []; + } + if (message.repeatedString) { + obj.repeatedString = message.repeatedString.map((e) => e); + } else { + obj.repeatedString = []; + } + if (message.repeatedNumber) { + obj.repeatedNumber = message.repeatedNumber.map((e) => Math.round(e)); + } else { + obj.repeatedNumber = []; + } + return obj; + }, + + create, I>>(base?: I): TPartial { + return TPartial.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): TPartial { + const message = createBaseTPartial(); + message.number = object.number ?? undefined; + message.string = object.string ?? undefined; + message.map = (object.map === undefined || object.map === null) + ? undefined + : Object.entries(object.map ?? {}).reduce<{ [key: string]: string }>((acc, [key, value]) => { + if (value !== undefined) { + acc[key] = String(value); + } + return acc; + }, {}); + message.message = (object.message !== undefined && object.message !== null) + ? TPartialMessage.fromPartial(object.message) + : undefined; + message.repeatedMessage = object.repeatedMessage?.map((e) => TPartialMessage.fromPartial(e)) || undefined; + message.repeatedString = object.repeatedString?.map((e) => e) || undefined; + message.repeatedNumber = object.repeatedNumber?.map((e) => e) || undefined; + return message; + }, +}; + +function createBaseTPartial_MapEntry(): TPartial_MapEntry { + return { key: "", value: "" }; +} + +export const TPartial_MapEntry = { + encode(message: TPartial_MapEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== "") { + writer.uint32(18).string(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TPartial_MapEntry { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTPartial_MapEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag != 10) { + break; + } + + message.key = reader.string(); + continue; + case 2: + if (tag != 18) { + break; + } + + message.value = reader.string(); + continue; + } + if ((tag & 7) == 4 || tag == 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TPartial_MapEntry { + return { key: isSet(object.key) ? String(object.key) : "", value: isSet(object.value) ? String(object.value) : "" }; + }, + + toJSON(message: TPartial_MapEntry): unknown { + const obj: any = {}; + message.key !== undefined && (obj.key = message.key); + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + create, I>>(base?: I): TPartial_MapEntry { + return TPartial_MapEntry.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): TPartial_MapEntry { + const message = createBaseTPartial_MapEntry(); + message.key = object.key ?? ""; + message.value = object.value ?? ""; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/use-optionals-no-undefined/test.ts b/integration/use-optionals-no-undefined/test.ts index 90e2395b3..4e6036247 100644 --- a/integration/use-optionals-no-undefined/test.ts +++ b/integration/use-optionals-no-undefined/test.ts @@ -520,20 +520,20 @@ export const OptionalsTest = { fromPartial, I>>(object: I): OptionalsTest { const message = createBaseOptionalsTest(); - message.id = object.id ?? 0; + message.id = object.id ?? undefined; message.child = (object.child !== undefined && object.child !== null) ? Child.fromPartial(object.child) : undefined; - message.state = object.state ?? 0; - message.long = object.long ?? 0; - message.truth = object.truth ?? false; - message.description = object.description ?? ""; - message.data = object.data ?? new Uint8Array(); - message.repId = object.repId?.map((e) => e) || []; - message.repChild = object.repChild?.map((e) => Child.fromPartial(e)) || []; - message.repState = object.repState?.map((e) => e) || []; - message.repLong = object.repLong?.map((e) => e) || []; - message.repTruth = object.repTruth?.map((e) => e) || []; - message.repDescription = object.repDescription?.map((e) => e) || []; - message.repData = object.repData?.map((e) => e) || []; + message.state = object.state ?? undefined; + message.long = object.long ?? undefined; + message.truth = object.truth ?? undefined; + message.description = object.description ?? undefined; + message.data = object.data ?? undefined; + message.repId = object.repId?.map((e) => e) || undefined; + message.repChild = object.repChild?.map((e) => Child.fromPartial(e)) || undefined; + message.repState = object.repState?.map((e) => e) || undefined; + message.repLong = object.repLong?.map((e) => e) || undefined; + message.repTruth = object.repTruth?.map((e) => e) || undefined; + message.repDescription = object.repDescription?.map((e) => e) || undefined; + message.repData = object.repData?.map((e) => e) || undefined; message.optId = object.optId ?? undefined; message.optChild = (object.optChild !== undefined && object.optChild !== null) ? Child.fromPartial(object.optChild) @@ -543,15 +543,14 @@ export const OptionalsTest = { message.optTruth = object.optTruth ?? undefined; message.optDescription = object.optDescription ?? undefined; message.optData = object.optData ?? undefined; - message.translations = Object.entries(object.translations ?? {}).reduce<{ [key: string]: string }>( - (acc, [key, value]) => { + message.translations = (object.translations === undefined || object.translations === null) + ? undefined + : Object.entries(object.translations ?? {}).reduce<{ [key: string]: string }>((acc, [key, value]) => { if (value !== undefined) { acc[key] = String(value); } return acc; - }, - {}, - ); + }, {}); return message; }, }; diff --git a/src/main.ts b/src/main.ts index 8d4b10086..17b3f77de 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1508,6 +1508,9 @@ function generateFromJson(ctx: Context, fullName: string, fullTypeName: string, } }; + const noDefaultValue = + !options.initializeFieldsAsUndefined && isOptionalProperty(field, messageDesc.options, options); + // and then use the snippet to handle repeated fields if necessary if (canonicalFromJson[fullTypeName]?.[fieldName]) { chunks.push(code`${fieldName}: ${canonicalFromJson[fullTypeName][fieldName]("object")},`); @@ -1845,15 +1848,22 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri } }; + const noDefaultValue = + !options.initializeFieldsAsUndefined && isOptionalProperty(field, messageDesc.options, options); + // and then use the snippet to handle repeated fields if necessary if (isRepeated(field)) { if (isMapType(ctx, messageDesc, field)) { const fieldType = toTypeName(ctx, messageDesc, field); const i = maybeCastToNumber(ctx, messageDesc, field, "key"); + const noValueSnippet = noDefaultValue + ? `(object.${fieldName} === undefined || object.${fieldName} === null) ? undefined : ` + : ""; + if (ctx.options.useMapType) { chunks.push(code` - message.${fieldName} = (() => { + message.${fieldName} = ${noValueSnippet} (() => { const m = new Map(); (object.${fieldName} as ${fieldType} ?? new Map()).forEach((value, key) => { if (value !== undefined) { @@ -1865,7 +1875,7 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri `); } else { chunks.push(code` - message.${fieldName} = Object.entries(object.${fieldName} ?? {}).reduce<${fieldType}>((acc, [key, value]) => { + message.${fieldName} = ${noValueSnippet} Object.entries(object.${fieldName} ?? {}).reduce<${fieldType}>((acc, [key, value]) => { if (value !== undefined) { acc[${i}] = ${readSnippet("value")}; } @@ -1874,8 +1884,10 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri `); } } else { + const fallback = noDefaultValue ? "undefined" : "[]"; + chunks.push(code` - message.${fieldName} = object.${fieldName}?.map((e) => ${readSnippet("e")}) || []; + message.${fieldName} = object.${fieldName}?.map((e) => ${readSnippet("e")}) || ${fallback}; `); } } else if (isWithinOneOfThatShouldBeUnion(options, field)) { @@ -1892,10 +1904,10 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri `); } else if (readSnippet(`x`).toCodeString([]) == "x") { // An optimized case of the else below that works when `readSnippet` returns the plain input - const fallback = isWithinOneOf(field) ? "undefined" : defaultValue(ctx, field); + const fallback = isWithinOneOf(field) || noDefaultValue ? "undefined" : defaultValue(ctx, field); chunks.push(code`message.${fieldName} = object.${fieldName} ?? ${fallback};`); } else { - const fallback = isWithinOneOf(field) ? "undefined" : defaultValue(ctx, field); + const fallback = isWithinOneOf(field) || noDefaultValue ? "undefined" : defaultValue(ctx, field); chunks.push(code` message.${fieldName} = (object.${fieldName} !== undefined && object.${fieldName} !== null) ? ${readSnippet(`object.${fieldName}`)} From 20f9c0ba832fc59981c9c6bcc59defd4ed7bb820 Mon Sep 17 00:00:00 2001 From: ilikdoge Date: Sun, 26 Mar 2023 17:38:26 -0700 Subject: [PATCH 2/3] feat: fromJSON do not default initialize Fixes https://github.com/stephenh/ts-proto/issues/682 --- .../from-partial-no-initialize/test.ts | 18 ++++++----- .../use-optionals-no-undefined/test.ts | 30 ++++++++++--------- src/main.ts | 14 ++++++--- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/integration/from-partial-no-initialize/test.ts b/integration/from-partial-no-initialize/test.ts index 0f3b545ac..67aa0de82 100644 --- a/integration/from-partial-no-initialize/test.ts +++ b/integration/from-partial-no-initialize/test.ts @@ -58,7 +58,7 @@ export const TPartialMessage = { }, fromJSON(object: any): TPartialMessage { - return { field: isSet(object.field) ? String(object.field) : "" }; + return { field: isSet(object.field) ? String(object.field) : undefined }; }, toJSON(message: TPartialMessage): unknown { @@ -211,20 +211,24 @@ export const TPartial = { fromJSON(object: any): TPartial { return { - number: isSet(object.number) ? Number(object.number) : 0, - string: isSet(object.string) ? String(object.string) : "", + number: isSet(object.number) ? Number(object.number) : undefined, + string: isSet(object.string) ? String(object.string) : undefined, map: isObject(object.map) ? Object.entries(object.map).reduce<{ [key: string]: string }>((acc, [key, value]) => { acc[key] = String(value); return acc; }, {}) - : {}, + : undefined, message: isSet(object.message) ? TPartialMessage.fromJSON(object.message) : undefined, repeatedMessage: Array.isArray(object?.repeatedMessage) ? object.repeatedMessage.map((e: any) => TPartialMessage.fromJSON(e)) - : [], - repeatedString: Array.isArray(object?.repeatedString) ? object.repeatedString.map((e: any) => String(e)) : [], - repeatedNumber: Array.isArray(object?.repeatedNumber) ? object.repeatedNumber.map((e: any) => Number(e)) : [], + : undefined, + repeatedString: Array.isArray(object?.repeatedString) + ? object.repeatedString.map((e: any) => String(e)) + : undefined, + repeatedNumber: Array.isArray(object?.repeatedNumber) + ? object.repeatedNumber.map((e: any) => Number(e)) + : undefined, }; }, diff --git a/integration/use-optionals-no-undefined/test.ts b/integration/use-optionals-no-undefined/test.ts index 4e6036247..6dabea008 100644 --- a/integration/use-optionals-no-undefined/test.ts +++ b/integration/use-optionals-no-undefined/test.ts @@ -421,20 +421,22 @@ export const OptionalsTest = { fromJSON(object: any): OptionalsTest { return { - id: isSet(object.id) ? Number(object.id) : 0, + id: isSet(object.id) ? Number(object.id) : undefined, child: isSet(object.child) ? Child.fromJSON(object.child) : undefined, - state: isSet(object.state) ? stateEnumFromJSON(object.state) : 0, - long: isSet(object.long) ? Number(object.long) : 0, - truth: isSet(object.truth) ? Boolean(object.truth) : false, - description: isSet(object.description) ? String(object.description) : "", - data: isSet(object.data) ? bytesFromBase64(object.data) : new Uint8Array(), - repId: Array.isArray(object?.repId) ? object.repId.map((e: any) => Number(e)) : [], - repChild: Array.isArray(object?.repChild) ? object.repChild.map((e: any) => Child.fromJSON(e)) : [], - repState: Array.isArray(object?.repState) ? object.repState.map((e: any) => stateEnumFromJSON(e)) : [], - repLong: Array.isArray(object?.repLong) ? object.repLong.map((e: any) => Number(e)) : [], - repTruth: Array.isArray(object?.repTruth) ? object.repTruth.map((e: any) => Boolean(e)) : [], - repDescription: Array.isArray(object?.repDescription) ? object.repDescription.map((e: any) => String(e)) : [], - repData: Array.isArray(object?.repData) ? object.repData.map((e: any) => bytesFromBase64(e)) : [], + state: isSet(object.state) ? stateEnumFromJSON(object.state) : undefined, + long: isSet(object.long) ? Number(object.long) : undefined, + truth: isSet(object.truth) ? Boolean(object.truth) : undefined, + description: isSet(object.description) ? String(object.description) : undefined, + data: isSet(object.data) ? bytesFromBase64(object.data) : undefined, + repId: Array.isArray(object?.repId) ? object.repId.map((e: any) => Number(e)) : undefined, + repChild: Array.isArray(object?.repChild) ? object.repChild.map((e: any) => Child.fromJSON(e)) : undefined, + repState: Array.isArray(object?.repState) ? object.repState.map((e: any) => stateEnumFromJSON(e)) : undefined, + repLong: Array.isArray(object?.repLong) ? object.repLong.map((e: any) => Number(e)) : undefined, + repTruth: Array.isArray(object?.repTruth) ? object.repTruth.map((e: any) => Boolean(e)) : undefined, + repDescription: Array.isArray(object?.repDescription) + ? object.repDescription.map((e: any) => String(e)) + : undefined, + repData: Array.isArray(object?.repData) ? object.repData.map((e: any) => bytesFromBase64(e)) : undefined, optId: isSet(object.optId) ? Number(object.optId) : undefined, optChild: isSet(object.optChild) ? Child.fromJSON(object.optChild) : undefined, optState: isSet(object.optState) ? stateEnumFromJSON(object.optState) : undefined, @@ -447,7 +449,7 @@ export const OptionalsTest = { acc[key] = String(value); return acc; }, {}) - : {}, + : undefined, }; }, diff --git a/src/main.ts b/src/main.ts index 17b3f77de..fa08b4480 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1520,32 +1520,38 @@ function generateFromJson(ctx: Context, fullName: string, fullTypeName: string, const i = maybeCastToNumber(ctx, messageDesc, field, "key"); if (ctx.options.useMapType) { + const fallback = noDefaultValue ? "undefined" : "new Map()"; + chunks.push(code` ${fieldName}: ${ctx.utils.isObject}(${jsonProperty}) ? Object.entries(${jsonProperty}).reduce<${fieldType}>((acc, [key, value]) => { acc.set(${i}, ${readSnippet("value")}); return acc; }, new Map()) - : new Map(), + : ${fallback}, `); } else { + const fallback = noDefaultValue ? "undefined" : "{}"; + chunks.push(code` ${fieldName}: ${ctx.utils.isObject}(${jsonProperty}) ? Object.entries(${jsonProperty}).reduce<${fieldType}>((acc, [key, value]) => { acc[${i}] = ${readSnippet("value")}; return acc; }, {}) - : {}, + : ${fallback}, `); } } else { + const fallback = noDefaultValue ? "undefined" : "[]"; + const readValueSnippet = readSnippet("e"); if (readValueSnippet.toString() === code`e`.toString()) { chunks.push(code`${fieldName}: Array.isArray(${jsonPropertyOptional}) ? [...${jsonProperty}] : [],`); } else { // Explicit `any` type required to make TS with noImplicitAny happy. `object` is also `any` here. chunks.push(code` - ${fieldName}: Array.isArray(${jsonPropertyOptional}) ? ${jsonProperty}.map((e: any) => ${readValueSnippet}): [], + ${fieldName}: Array.isArray(${jsonPropertyOptional}) ? ${jsonProperty}.map((e: any) => ${readValueSnippet}): ${fallback}, `); } } @@ -1584,7 +1590,7 @@ function generateFromJson(ctx: Context, fullName: string, fullTypeName: string, : undefined, `); } else { - const fallback = isWithinOneOf(field) ? "undefined" : defaultValue(ctx, field); + const fallback = isWithinOneOf(field) || noDefaultValue ? "undefined" : defaultValue(ctx, field); chunks.push(code` ${fieldName}: ${ctx.utils.isSet}(${jsonProperty}) ? ${readSnippet(`${jsonProperty}`)} From add0d05774fb5f72aaf98902e63bacf7f092807f Mon Sep 17 00:00:00 2001 From: ilikdoge Date: Mon, 27 Mar 2023 00:52:36 -0700 Subject: [PATCH 3/3] test: integration --- integration/groups/test.ts | 65 +++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/integration/groups/test.ts b/integration/groups/test.ts index d420ba57e..36ec4f513 100644 --- a/integration/groups/test.ts +++ b/integration/groups/test.ts @@ -136,9 +136,9 @@ export const GroupsOptionalTest = { fromJSON(object: any): GroupsOptionalTest { return { - int1: isSet(object.int1) ? Number(object.int1) : 0, + int1: isSet(object.int1) ? Number(object.int1) : undefined, group: isSet(object.group) ? GroupsOptionalTest_Group.fromJSON(object.group) : undefined, - int3: isSet(object.int3) ? Number(object.int3) : 0, + int3: isSet(object.int3) ? Number(object.int3) : undefined, }; }, @@ -157,11 +157,11 @@ export const GroupsOptionalTest = { fromPartial, I>>(object: I): GroupsOptionalTest { const message = createBaseGroupsOptionalTest(); - message.int1 = object.int1 ?? 0; + message.int1 = object.int1 ?? undefined; message.group = (object.group !== undefined && object.group !== null) ? GroupsOptionalTest_Group.fromPartial(object.group) : undefined; - message.int3 = object.int3 ?? 0; + message.int3 = object.int3 ?? undefined; return message; }, }; @@ -240,7 +240,10 @@ export const GroupsOptionalTest_Group = { }, fromJSON(object: any): GroupsOptionalTest_Group { - return { key: isSet(object.key) ? String(object.key) : "", value: isSet(object.value) ? String(object.value) : "" }; + return { + key: isSet(object.key) ? String(object.key) : undefined, + value: isSet(object.value) ? String(object.value) : undefined, + }; }, toJSON(message: GroupsOptionalTest_Group): unknown { @@ -256,8 +259,8 @@ export const GroupsOptionalTest_Group = { fromPartial, I>>(object: I): GroupsOptionalTest_Group { const message = createBaseGroupsOptionalTest_Group(); - message.key = object.key ?? ""; - message.value = object.value ?? ""; + message.key = object.key ?? undefined; + message.value = object.value ?? undefined; return message; }, }; @@ -390,9 +393,11 @@ export const GroupsRepeatedTest = { fromJSON(object: any): GroupsRepeatedTest { return { - int1: Array.isArray(object?.int1) ? object.int1.map((e: any) => Number(e)) : [], - group: Array.isArray(object?.group) ? object.group.map((e: any) => GroupsRepeatedTest_Group.fromJSON(e)) : [], - int3: Array.isArray(object?.int3) ? object.int3.map((e: any) => Number(e)) : [], + int1: Array.isArray(object?.int1) ? object.int1.map((e: any) => Number(e)) : undefined, + group: Array.isArray(object?.group) + ? object.group.map((e: any) => GroupsRepeatedTest_Group.fromJSON(e)) + : undefined, + int3: Array.isArray(object?.int3) ? object.int3.map((e: any) => Number(e)) : undefined, }; }, @@ -422,9 +427,9 @@ export const GroupsRepeatedTest = { fromPartial, I>>(object: I): GroupsRepeatedTest { const message = createBaseGroupsRepeatedTest(); - message.int1 = object.int1?.map((e) => e) || []; - message.group = object.group?.map((e) => GroupsRepeatedTest_Group.fromPartial(e)) || []; - message.int3 = object.int3?.map((e) => e) || []; + message.int1 = object.int1?.map((e) => e) || undefined; + message.group = object.group?.map((e) => GroupsRepeatedTest_Group.fromPartial(e)) || undefined; + message.int3 = object.int3?.map((e) => e) || undefined; return message; }, }; @@ -514,8 +519,8 @@ export const GroupsRepeatedTest_Group = { fromJSON(object: any): GroupsRepeatedTest_Group { return { - key: Array.isArray(object?.key) ? object.key.map((e: any) => String(e)) : [], - value: Array.isArray(object?.value) ? object.value.map((e: any) => String(e)) : [], + key: Array.isArray(object?.key) ? object.key.map((e: any) => String(e)) : undefined, + value: Array.isArray(object?.value) ? object.value.map((e: any) => String(e)) : undefined, }; }, @@ -540,8 +545,8 @@ export const GroupsRepeatedTest_Group = { fromPartial, I>>(object: I): GroupsRepeatedTest_Group { const message = createBaseGroupsRepeatedTest_Group(); - message.key = object.key?.map((e) => e) || []; - message.value = object.value?.map((e) => e) || []; + message.key = object.key?.map((e) => e) || undefined; + message.value = object.value?.map((e) => e) || undefined; return message; }, }; @@ -674,9 +679,11 @@ export const GroupsNestedTest = { fromJSON(object: any): GroupsNestedTest { return { - int1: Array.isArray(object?.int1) ? object.int1.map((e: any) => Number(e)) : [], - group: Array.isArray(object?.group) ? object.group.map((e: any) => GroupsNestedTest_Group.fromJSON(e)) : [], - int3: Array.isArray(object?.int3) ? object.int3.map((e: any) => Number(e)) : [], + int1: Array.isArray(object?.int1) ? object.int1.map((e: any) => Number(e)) : undefined, + group: Array.isArray(object?.group) + ? object.group.map((e: any) => GroupsNestedTest_Group.fromJSON(e)) + : undefined, + int3: Array.isArray(object?.int3) ? object.int3.map((e: any) => Number(e)) : undefined, }; }, @@ -706,9 +713,9 @@ export const GroupsNestedTest = { fromPartial, I>>(object: I): GroupsNestedTest { const message = createBaseGroupsNestedTest(); - message.int1 = object.int1?.map((e) => e) || []; - message.group = object.group?.map((e) => GroupsNestedTest_Group.fromPartial(e)) || []; - message.int3 = object.int3?.map((e) => e) || []; + message.int1 = object.int1?.map((e) => e) || undefined; + message.group = object.group?.map((e) => GroupsNestedTest_Group.fromPartial(e)) || undefined; + message.int3 = object.int3?.map((e) => e) || undefined; return message; }, }; @@ -785,7 +792,7 @@ export const GroupsNestedTest_Group = { return { nested: Array.isArray(object?.nested) ? object.nested.map((e: any) => GroupsNestedTest_Group_Nested.fromJSON(e)) - : [], + : undefined, }; }, @@ -805,7 +812,7 @@ export const GroupsNestedTest_Group = { fromPartial, I>>(object: I): GroupsNestedTest_Group { const message = createBaseGroupsNestedTest_Group(); - message.nested = object.nested?.map((e) => GroupsNestedTest_Group_Nested.fromPartial(e)) || []; + message.nested = object.nested?.map((e) => GroupsNestedTest_Group_Nested.fromPartial(e)) || undefined; return message; }, }; @@ -882,7 +889,7 @@ export const GroupsNestedTest_Group_Nested = { return { nested2: Array.isArray(object?.nested2) ? object.nested2.map((e: any) => GroupsNestedTest_Group_Nested_Nested2.fromJSON(e)) - : [], + : undefined, }; }, @@ -904,7 +911,7 @@ export const GroupsNestedTest_Group_Nested = { object: I, ): GroupsNestedTest_Group_Nested { const message = createBaseGroupsNestedTest_Group_Nested(); - message.nested2 = object.nested2?.map((e) => GroupsNestedTest_Group_Nested_Nested2.fromPartial(e)) || []; + message.nested2 = object.nested2?.map((e) => GroupsNestedTest_Group_Nested_Nested2.fromPartial(e)) || undefined; return message; }, }; @@ -973,7 +980,7 @@ export const GroupsNestedTest_Group_Nested_Nested2 = { }, fromJSON(object: any): GroupsNestedTest_Group_Nested_Nested2 { - return { string1: isSet(object.string1) ? String(object.string1) : "" }; + return { string1: isSet(object.string1) ? String(object.string1) : undefined }; }, toJSON(message: GroupsNestedTest_Group_Nested_Nested2): unknown { @@ -992,7 +999,7 @@ export const GroupsNestedTest_Group_Nested_Nested2 = { object: I, ): GroupsNestedTest_Group_Nested_Nested2 { const message = createBaseGroupsNestedTest_Group_Nested_Nested2(); - message.string1 = object.string1 ?? ""; + message.string1 = object.string1 ?? undefined; return message; }, };