diff --git a/integration/simple-long/google/protobuf/wrappers.bin b/integration/simple-long/google/protobuf/wrappers.bin index ba0182a94..136184cf8 100644 Binary files a/integration/simple-long/google/protobuf/wrappers.bin and b/integration/simple-long/google/protobuf/wrappers.bin differ diff --git a/integration/simple-long/google/protobuf/wrappers.ts b/integration/simple-long/google/protobuf/wrappers.ts index 3605ad6ae..26ad3cd97 100644 --- a/integration/simple-long/google/protobuf/wrappers.ts +++ b/integration/simple-long/google/protobuf/wrappers.ts @@ -230,11 +230,7 @@ export const Int64Value = { fromPartial(object: DeepPartial): Int64Value { const message = { ...baseInt64Value } as Int64Value; - if (object.value !== undefined && object.value !== null) { - message.value = object.value as Long; - } else { - message.value = Long.ZERO; - } + message.value = object.value !== undefined && object.value !== null ? Long.fromValue(object.value) : Long.ZERO; return message; }, }; @@ -281,11 +277,7 @@ export const UInt64Value = { fromPartial(object: DeepPartial): UInt64Value { const message = { ...baseUInt64Value } as UInt64Value; - if (object.value !== undefined && object.value !== null) { - message.value = object.value as Long; - } else { - message.value = Long.UZERO; - } + message.value = object.value !== undefined && object.value !== null ? Long.fromValue(object.value) : Long.UZERO; return message; }, }; @@ -560,9 +552,11 @@ function base64FromBytes(arr: Uint8Array): string { return btoa(bin.join('')); } -type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined | Long; +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; export type DeepPartial = T extends Builtin ? T + : T extends Long + ? string | number | Long : T extends Array ? Array> : T extends ReadonlyArray diff --git a/integration/simple-long/simple-test.ts b/integration/simple-long/simple-test.ts new file mode 100644 index 000000000..79f942076 --- /dev/null +++ b/integration/simple-long/simple-test.ts @@ -0,0 +1,76 @@ +import { SimpleWithMap } from './simple'; + +describe('simple', () => { + it('can fromPartial maps', () => { + const s1 = SimpleWithMap.fromPartial({ + intLookup: { 1: 2, 2: 1 }, + longLookup: { '1': 2, '2': 1 }, + }); + expect(s1).toMatchInlineSnapshot(` + Object { + "intLookup": Object { + "1": 2, + "2": 1, + }, + "longLookup": Object { + "1": Long { + "high": 0, + "low": 2, + "unsigned": false, + }, + "2": Long { + "high": 0, + "low": 1, + "unsigned": false, + }, + }, + "nameLookup": Object {}, + } + `); + }); + + it('can toJSON/fromJSON maps', () => { + const s1 = SimpleWithMap.fromPartial({ + intLookup: { 1: 2, 2: 1 }, + longLookup: { '1': 2, '2': 1 }, + }); + + const json = SimpleWithMap.toJSON(s1); + expect(json).toMatchInlineSnapshot(` + Object { + "intLookup": Object { + "1": 2, + "2": 1, + }, + "longLookup": Object { + "1": "2", + "2": "1", + }, + "nameLookup": Object {}, + } + `); + + const s2 = SimpleWithMap.fromJSON(JSON.parse(JSON.stringify(json))); + expect(s2).toMatchInlineSnapshot(` + Object { + "intLookup": Object { + "1": 2, + "2": 1, + }, + "longLookup": Object { + "1": Long { + "high": 0, + "low": 2, + "unsigned": false, + }, + "2": Long { + "high": 0, + "low": 1, + "unsigned": false, + }, + }, + "nameLookup": Object {}, + } + `); + }); +}); diff --git a/integration/simple-long/simple.bin b/integration/simple-long/simple.bin index 427793dea..b1ad8ee48 100644 Binary files a/integration/simple-long/simple.bin and b/integration/simple-long/simple.bin differ diff --git a/integration/simple-long/simple.proto b/integration/simple-long/simple.proto index c7bbc8419..9c812fc70 100644 --- a/integration/simple-long/simple.proto +++ b/integration/simple-long/simple.proto @@ -14,6 +14,8 @@ message SimpleWithWrappers { message SimpleWithMap { map nameLookup = 2; map intLookup = 3; + // Ideally we'd test map but we present maps as JS objects and `Long` cannot be used as a keys. + map longLookup = 4; } message Numbers { diff --git a/integration/simple-long/simple.ts b/integration/simple-long/simple.ts index bc06e402b..55dfae0da 100644 --- a/integration/simple-long/simple.ts +++ b/integration/simple-long/simple.ts @@ -17,6 +17,8 @@ export interface SimpleWithWrappers { export interface SimpleWithMap { nameLookup: { [key: string]: string }; intLookup: { [key: number]: number }; + /** Ideally we'd test map but we present maps as JS objects and `Long` cannot be used as a keys. */ + longLookup: { [key: string]: Long }; } export interface SimpleWithMap_NameLookupEntry { @@ -29,6 +31,11 @@ export interface SimpleWithMap_IntLookupEntry { value: number; } +export interface SimpleWithMap_LongLookupEntry { + key: string; + value: Long; +} + export interface Numbers { double: number; float: number; @@ -142,11 +149,8 @@ export const SimpleWithWrappers = { message.name = object.name ?? undefined; message.age = object.age ?? undefined; message.enabled = object.enabled ?? undefined; - if (object.bananas !== undefined && object.bananas !== null) { - message.bananas = object.bananas as Long | undefined; - } else { - message.bananas = undefined; - } + message.bananas = + object.bananas !== undefined && object.bananas !== null ? Long.fromValue(object.bananas) : undefined; message.coins = (object.coins ?? []).map((e) => e); message.snacks = (object.snacks ?? []).map((e) => e); return message; @@ -163,6 +167,9 @@ export const SimpleWithMap = { Object.entries(message.intLookup).forEach(([key, value]) => { SimpleWithMap_IntLookupEntry.encode({ key: key as any, value }, writer.uint32(26).fork()).ldelim(); }); + Object.entries(message.longLookup).forEach(([key, value]) => { + SimpleWithMap_LongLookupEntry.encode({ key: key as any, value }, writer.uint32(34).fork()).ldelim(); + }); return writer; }, @@ -172,6 +179,7 @@ export const SimpleWithMap = { const message = { ...baseSimpleWithMap } as SimpleWithMap; message.nameLookup = {}; message.intLookup = {}; + message.longLookup = {}; while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -187,6 +195,12 @@ export const SimpleWithMap = { message.intLookup[entry3.key] = entry3.value; } break; + case 4: + const entry4 = SimpleWithMap_LongLookupEntry.decode(reader, reader.uint32()); + if (entry4.value !== undefined) { + message.longLookup[entry4.key] = entry4.value; + } + break; default: reader.skipType(tag & 7); break; @@ -209,6 +223,12 @@ export const SimpleWithMap = { message.intLookup[Number(key)] = Number(value); }); } + message.longLookup = {}; + if (object.longLookup !== undefined && object.longLookup !== null) { + Object.entries(object.longLookup).forEach(([key, value]) => { + message.longLookup[key] = Long.fromString(value as string); + }); + } return message; }, @@ -226,6 +246,12 @@ export const SimpleWithMap = { obj.intLookup[k] = v; }); } + obj.longLookup = {}; + if (message.longLookup) { + Object.entries(message.longLookup).forEach(([k, v]) => { + obj.longLookup[k] = v.toString(); + }); + } return obj; }, @@ -247,6 +273,14 @@ export const SimpleWithMap = { } }); } + message.longLookup = {}; + if (object.longLookup !== undefined && object.longLookup !== null) { + Object.entries(object.longLookup).forEach(([key, value]) => { + if (value !== undefined) { + message.longLookup[key] = Long.fromValue(value); + } + }); + } return message; }, }; @@ -363,6 +397,62 @@ export const SimpleWithMap_IntLookupEntry = { }, }; +const baseSimpleWithMap_LongLookupEntry: object = { key: '', value: Long.ZERO }; + +export const SimpleWithMap_LongLookupEntry = { + encode(message: SimpleWithMap_LongLookupEntry, writer: Writer = Writer.create()): Writer { + if (message.key !== '') { + writer.uint32(10).string(message.key); + } + if (!message.value.isZero()) { + writer.uint32(16).int64(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): SimpleWithMap_LongLookupEntry { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = reader.string(); + break; + case 2: + message.value = reader.int64() as Long; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): SimpleWithMap_LongLookupEntry { + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + message.key = object.key !== undefined && object.key !== null ? String(object.key) : ''; + message.value = object.value !== undefined && object.value !== null ? Long.fromString(object.value) : Long.ZERO; + return message; + }, + + toJSON(message: SimpleWithMap_LongLookupEntry): unknown { + const obj: any = {}; + message.key !== undefined && (obj.key = message.key); + message.value !== undefined && (obj.value = (message.value || Long.ZERO).toString()); + return obj; + }, + + fromPartial(object: DeepPartial): SimpleWithMap_LongLookupEntry { + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + message.key = object.key ?? ''; + message.value = object.value !== undefined && object.value !== null ? Long.fromValue(object.value) : Long.ZERO; + return message; + }, +}; + const baseNumbers: object = { double: 0, float: 0, @@ -535,43 +625,27 @@ export const Numbers = { message.double = object.double ?? 0; message.float = object.float ?? 0; message.int32 = object.int32 ?? 0; - if (object.int64 !== undefined && object.int64 !== null) { - message.int64 = object.int64 as Long; - } else { - message.int64 = Long.ZERO; - } + message.int64 = object.int64 !== undefined && object.int64 !== null ? Long.fromValue(object.int64) : Long.ZERO; message.uint32 = object.uint32 ?? 0; - if (object.uint64 !== undefined && object.uint64 !== null) { - message.uint64 = object.uint64 as Long; - } else { - message.uint64 = Long.UZERO; - } + message.uint64 = object.uint64 !== undefined && object.uint64 !== null ? Long.fromValue(object.uint64) : Long.UZERO; message.sint32 = object.sint32 ?? 0; - if (object.sint64 !== undefined && object.sint64 !== null) { - message.sint64 = object.sint64 as Long; - } else { - message.sint64 = Long.ZERO; - } + message.sint64 = object.sint64 !== undefined && object.sint64 !== null ? Long.fromValue(object.sint64) : Long.ZERO; message.fixed32 = object.fixed32 ?? 0; - if (object.fixed64 !== undefined && object.fixed64 !== null) { - message.fixed64 = object.fixed64 as Long; - } else { - message.fixed64 = Long.UZERO; - } + message.fixed64 = + object.fixed64 !== undefined && object.fixed64 !== null ? Long.fromValue(object.fixed64) : Long.UZERO; message.sfixed32 = object.sfixed32 ?? 0; - if (object.sfixed64 !== undefined && object.sfixed64 !== null) { - message.sfixed64 = object.sfixed64 as Long; - } else { - message.sfixed64 = Long.ZERO; - } - message.manyUint64 = (object.manyUint64 ?? []).map((e) => e); + message.sfixed64 = + object.sfixed64 !== undefined && object.sfixed64 !== null ? Long.fromValue(object.sfixed64) : Long.ZERO; + message.manyUint64 = (object.manyUint64 ?? []).map((e) => Long.fromValue(e)); return message; }, }; -type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined | Long; +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; export type DeepPartial = T extends Builtin ? T + : T extends Long + ? string | number | Long : T extends Array ? Array> : T extends ReadonlyArray diff --git a/integration/simple/simple-json-test.ts b/integration/simple/simple-json-test.ts index 7891a5f5d..cf3d6059f 100644 --- a/integration/simple/simple-json-test.ts +++ b/integration/simple/simple-json-test.ts @@ -190,6 +190,7 @@ describe('simple json', () => { "intLookup": Object { "1": 0, }, + "longLookup": Object {}, "mapOfBytes": Object {}, "mapOfStringValues": Object {}, "mapOfTimestamps": Object {}, @@ -212,6 +213,7 @@ describe('simple json', () => { Object { "entitiesById": Object {}, "intLookup": Object {}, + "longLookup": Object {}, "mapOfBytes": Object {}, "mapOfStringValues": Object {}, "mapOfTimestamps": Object { @@ -236,12 +238,14 @@ describe('simple json', () => { b: new Uint8Array([1, 2, 3]), }, mapOfStringValues: {}, + longLookup: {}, }; const json = SimpleWithMap.toJSON(s1); expect(json).toMatchInlineSnapshot(` Object { "entitiesById": Object {}, "intLookup": Object {}, + "longLookup": Object {}, "mapOfBytes": Object { "a": "AQI=", "b": "AQID", @@ -265,6 +269,7 @@ describe('simple json', () => { Object { "entitiesById": Object {}, "intLookup": Object {}, + "longLookup": Object {}, "mapOfBytes": Object { "a": Uint8Array [ 1, diff --git a/integration/simple/simple-test.ts b/integration/simple/simple-test.ts index f6d6c79a9..f87be3048 100644 --- a/integration/simple/simple-test.ts +++ b/integration/simple/simple-test.ts @@ -232,8 +232,10 @@ describe('simple', () => { mapOfTimestamps: {}, mapOfBytes: {}, mapOfStringValues: { a: '1', b: '', c: undefined }, + longLookup: { 1: 2, 2: 1 }, }; - const s2 = PbSimpleWithMap.toObject(PbSimpleWithMap.decode(SimpleWithMap.encode(s1).finish())); + // const s2 = PbSimpleWithMap.toObject(PbSimpleWithMap.decode(SimpleWithMap.encode(s1).finish())); + const s2 = SimpleWithMap.decode(SimpleWithMap.encode(s1).finish()); expect(s2).toMatchInlineSnapshot(` Object { "entitiesById": Object { @@ -248,12 +250,16 @@ describe('simple', () => { "1": 2, "2": 1, }, + "longLookup": Object { + "1": 2, + "2": 1, + }, + "mapOfBytes": Object {}, "mapOfStringValues": Object { - "a": Object { - "value": "1", - }, - "b": Object {}, + "a": "1", + "b": "", }, + "mapOfTimestamps": Object {}, "nameLookup": Object { "foo": "bar", }, @@ -269,6 +275,7 @@ describe('simple', () => { mapOfTimestamps: {}, mapOfBytes: {}, mapOfStringValues: { foo: '', bar: undefined }, + longLookup: { 1: 2, 2: 1 }, }; const s2 = SimpleWithMap.decode(SimpleWithMap.encode(s1).finish()); expect(s2).toEqual(s1); @@ -317,6 +324,7 @@ describe('simple', () => { const s1 = SimpleWithMap.fromPartial({ intLookup: { 1: 2, 2: 0 }, mapOfStringValues: { a: '1', b: '', c: undefined }, + longLookup: { 1: 2, 2: 0 }, }); expect(s1).toMatchInlineSnapshot(` Object { @@ -325,6 +333,10 @@ describe('simple', () => { "1": 2, "2": 0, }, + "longLookup": Object { + "1": 2, + "2": 0, + }, "mapOfBytes": Object {}, "mapOfStringValues": Object { "a": "1", diff --git a/integration/simple/simple.bin b/integration/simple/simple.bin index 8ebaa258b..eb98fb18e 100644 Binary files a/integration/simple/simple.bin and b/integration/simple/simple.bin differ diff --git a/integration/simple/simple.proto b/integration/simple/simple.proto index a7c6dad79..98ad06038 100644 --- a/integration/simple/simple.proto +++ b/integration/simple/simple.proto @@ -97,6 +97,7 @@ message SimpleWithMap { map mapOfTimestamps = 4; map mapOfBytes = 5; map mapOfStringValues = 6; + map longLookup = 7; } message SimpleWithSnakeCaseMap { diff --git a/integration/simple/simple.ts b/integration/simple/simple.ts index 7a35e3d16..f2329834f 100644 --- a/integration/simple/simple.ts +++ b/integration/simple/simple.ts @@ -194,6 +194,7 @@ export interface SimpleWithMap { mapOfTimestamps: { [key: string]: Date }; mapOfBytes: { [key: string]: Uint8Array }; mapOfStringValues: { [key: string]: string | undefined }; + longLookup: { [key: number]: number }; } export interface SimpleWithMap_EntitiesByIdEntry { @@ -226,6 +227,11 @@ export interface SimpleWithMap_MapOfStringValuesEntry { value: string | undefined; } +export interface SimpleWithMap_LongLookupEntry { + key: number; + value: number; +} + export interface SimpleWithSnakeCaseMap { entitiesById: { [key: number]: Entity }; } @@ -957,6 +963,9 @@ export const SimpleWithMap = { SimpleWithMap_MapOfStringValuesEntry.encode({ key: key as any, value }, writer.uint32(50).fork()).ldelim(); } }); + Object.entries(message.longLookup).forEach(([key, value]) => { + SimpleWithMap_LongLookupEntry.encode({ key: key as any, value }, writer.uint32(58).fork()).ldelim(); + }); return writer; }, @@ -970,6 +979,7 @@ export const SimpleWithMap = { message.mapOfTimestamps = {}; message.mapOfBytes = {}; message.mapOfStringValues = {}; + message.longLookup = {}; while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -1009,6 +1019,12 @@ export const SimpleWithMap = { message.mapOfStringValues[entry6.key] = entry6.value; } break; + case 7: + const entry7 = SimpleWithMap_LongLookupEntry.decode(reader, reader.uint32()); + if (entry7.value !== undefined) { + message.longLookup[entry7.key] = entry7.value; + } + break; default: reader.skipType(tag & 7); break; @@ -1055,6 +1071,12 @@ export const SimpleWithMap = { message.mapOfStringValues[key] = value as string | undefined; }); } + message.longLookup = {}; + if (object.longLookup !== undefined && object.longLookup !== null) { + Object.entries(object.longLookup).forEach(([key, value]) => { + message.longLookup[Number(key)] = Number(value); + }); + } return message; }, @@ -1096,6 +1118,12 @@ export const SimpleWithMap = { obj.mapOfStringValues[k] = v; }); } + obj.longLookup = {}; + if (message.longLookup) { + Object.entries(message.longLookup).forEach(([k, v]) => { + obj.longLookup[k] = v; + }); + } return obj; }, @@ -1149,6 +1177,14 @@ export const SimpleWithMap = { } }); } + message.longLookup = {}; + if (object.longLookup !== undefined && object.longLookup !== null) { + Object.entries(object.longLookup).forEach(([key, value]) => { + if (value !== undefined) { + message.longLookup[Number(key)] = Number(value); + } + }); + } return message; }, }; @@ -1492,6 +1528,62 @@ export const SimpleWithMap_MapOfStringValuesEntry = { }, }; +const baseSimpleWithMap_LongLookupEntry: object = { key: 0, value: 0 }; + +export const SimpleWithMap_LongLookupEntry = { + encode(message: SimpleWithMap_LongLookupEntry, writer: Writer = Writer.create()): Writer { + if (message.key !== 0) { + writer.uint32(8).int64(message.key); + } + if (message.value !== 0) { + writer.uint32(16).int64(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): SimpleWithMap_LongLookupEntry { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = longToNumber(reader.int64() as Long); + break; + case 2: + message.value = longToNumber(reader.int64() as Long); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): SimpleWithMap_LongLookupEntry { + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + message.key = object.key !== undefined && object.key !== null ? Number(object.key) : 0; + message.value = object.value !== undefined && object.value !== null ? Number(object.value) : 0; + return message; + }, + + toJSON(message: SimpleWithMap_LongLookupEntry): unknown { + const obj: any = {}; + message.key !== undefined && (obj.key = message.key); + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): SimpleWithMap_LongLookupEntry { + const message = { ...baseSimpleWithMap_LongLookupEntry } as SimpleWithMap_LongLookupEntry; + message.key = object.key ?? 0; + message.value = object.value ?? 0; + return message; + }, +}; + const baseSimpleWithSnakeCaseMap: object = {}; export const SimpleWithSnakeCaseMap = { diff --git a/src/main.ts b/src/main.ts index ea3744a18..276217339 100644 --- a/src/main.ts +++ b/src/main.ts @@ -399,16 +399,19 @@ function makeDeepPartial(options: Options, longs: ReturnType` : code`keyof T`; // Based on the type from ts-essentials const DeepPartial = conditionalOutput( 'DeepPartial', code` - type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined${maybeLong}; - ${maybeExport} type DeepPartial = T extends Builtin + type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + ${maybeExport} type DeepPartial = T extends Builtin ? T + ${maybeLong} : T extends Array ? Array> : T extends ReadonlyArray @@ -952,6 +955,8 @@ function generateFromJson(ctx: Context, fullName: string, messageDesc: Descripto } else { return code`${utils.bytesFromBase64}(${from} as string)`; } + } else if (isLong(valueType) && options.forceLong === LongOption.LONG) { + return code`Long.fromString(${from} as string)`; } else if (isEnum(valueType)) { return code`${from} as number`; } else { @@ -1061,6 +1066,8 @@ function generateToJson(ctx: Context, fullName: string, messageDesc: DescriptorP return code`${from}`; } else if (isTimestamp(valueType) && options.useDate === DateOption.TIMESTAMP) { return code`${utils.fromTimestamp}(${from}).toISOString()`; + } else if (isLong(valueType) && options.forceLong === LongOption.LONG) { + return code`${from}.toString()`; } else if (isScalar(valueType) || isValueType(ctx, valueType)) { return code`${from}`; } else { @@ -1134,7 +1141,9 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri const fieldName = maybeSnakeToCamel(field.name, options); const readSnippet = (from: string): Code => { - if ( + if ((isLong(field) || isLongValueType(field)) && options.forceLong === LongOption.LONG) { + return code`Long.fromValue(${from})`; + } else if ( isPrimitive(field) || (isTimestamp(field) && (options.useDate === DateOption.DATE || options.useDate === DateOption.STRING)) || isValueType(ctx, field) @@ -1148,6 +1157,8 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri return code`${from}`; } else if (isEnum(valueType)) { return code`${from} as number`; + } else if (isLong(valueType) && options.forceLong === LongOption.LONG) { + return code`Long.fromValue(${from})`; } else { const cstr = capitalize(basicTypeName(ctx, valueType).toCodeString()); return code`${cstr}(${from})`; @@ -1203,15 +1214,6 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri message.${oneofName} = { $case: '${fieldName}', ${fieldName}: ${v} }; } `); - } else if ((isLong(field) || isLongValueType(field)) && options.forceLong === LongOption.LONG) { - const v = readSnippet(`object.${fieldName}`); - const type = basicTypeName(ctx, field); - chunks.push(code`if (object.${fieldName} !== undefined && object.${fieldName} !== null) {`); - chunks.push(code`message.${fieldName} = ${v} as ${type};`); - chunks.push(code`} else {`); - const fallback = isWithinOneOf(field) ? 'undefined' : defaultValue(ctx, field); - chunks.push(code`message.${fieldName} = ${fallback}`); - chunks.push(code`}`); } 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);