@azure-tools/typespec-ts 0.29.0-alpha.20240626.1 → 0.30.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/src/contextManager.d.ts +70 -0
  3. package/dist/src/contextManager.d.ts.map +1 -0
  4. package/dist/src/contextManager.js +59 -0
  5. package/dist/src/contextManager.js.map +1 -0
  6. package/dist/src/index.d.ts.map +1 -1
  7. package/dist/src/index.js +13 -2
  8. package/dist/src/index.js.map +1 -1
  9. package/dist/src/metaTree.d.ts +12 -0
  10. package/dist/src/metaTree.d.ts.map +1 -0
  11. package/dist/src/metaTree.js +2 -0
  12. package/dist/src/metaTree.js.map +1 -0
  13. package/dist/src/modular/buildClassicalClient.js +3 -1
  14. package/dist/src/modular/buildClassicalClient.js.map +1 -1
  15. package/dist/src/modular/buildCodeModel.d.ts.map +1 -1
  16. package/dist/src/modular/buildCodeModel.js +17 -7
  17. package/dist/src/modular/buildCodeModel.js.map +1 -1
  18. package/dist/src/modular/buildHelperSerializers.d.ts +3 -0
  19. package/dist/src/modular/buildHelperSerializers.d.ts.map +1 -0
  20. package/dist/src/modular/buildHelperSerializers.js +60 -0
  21. package/dist/src/modular/buildHelperSerializers.js.map +1 -0
  22. package/dist/src/modular/buildOperations.d.ts.map +1 -1
  23. package/dist/src/modular/buildOperations.js +5 -0
  24. package/dist/src/modular/buildOperations.js.map +1 -1
  25. package/dist/src/modular/buildSerializeUtils.d.ts.map +1 -1
  26. package/dist/src/modular/buildSerializeUtils.js +11 -4
  27. package/dist/src/modular/buildSerializeUtils.js.map +1 -1
  28. package/dist/src/modular/buildSubpathIndex.d.ts.map +1 -1
  29. package/dist/src/modular/buildSubpathIndex.js +6 -0
  30. package/dist/src/modular/buildSubpathIndex.js.map +1 -1
  31. package/dist/src/modular/emitModels.d.ts +5 -5
  32. package/dist/src/modular/emitModels.d.ts.map +1 -1
  33. package/dist/src/modular/emitModels.js +32 -4
  34. package/dist/src/modular/emitModels.js.map +1 -1
  35. package/dist/src/modular/helpers/operationHelpers.d.ts +10 -2
  36. package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
  37. package/dist/src/modular/helpers/operationHelpers.js +135 -56
  38. package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
  39. package/dist/src/modular/helpers/typeHelpers.d.ts +1 -0
  40. package/dist/src/modular/helpers/typeHelpers.d.ts.map +1 -1
  41. package/dist/src/modular/helpers/typeHelpers.js +24 -19
  42. package/dist/src/modular/helpers/typeHelpers.js.map +1 -1
  43. package/dist/src/modular/modularCodeModel.d.ts +2 -2
  44. package/dist/src/modular/modularCodeModel.d.ts.map +1 -1
  45. package/dist/src/modular/serialization/buildSerializerFunction.d.ts +4 -0
  46. package/dist/src/modular/serialization/buildSerializerFunction.d.ts.map +1 -0
  47. package/dist/src/modular/serialization/buildSerializerFunction.js +160 -0
  48. package/dist/src/modular/serialization/buildSerializerFunction.js.map +1 -0
  49. package/dist/src/transform/transformSchemas.d.ts.map +1 -1
  50. package/dist/src/transform/transformSchemas.js +3 -0
  51. package/dist/src/transform/transformSchemas.js.map +1 -1
  52. package/dist/src/utils/importHelper.d.ts +10 -0
  53. package/dist/src/utils/importHelper.d.ts.map +1 -0
  54. package/dist/src/utils/importHelper.js +54 -0
  55. package/dist/src/utils/importHelper.js.map +1 -0
  56. package/dist/tsconfig.tsbuildinfo +1 -1
  57. package/package.json +4 -4
  58. package/src/contextManager.ts +99 -0
  59. package/src/index.ts +14 -2
  60. package/src/metaTree.ts +14 -0
  61. package/src/modular/buildClassicalClient.ts +5 -1
  62. package/src/modular/buildCodeModel.ts +22 -6
  63. package/src/modular/buildHelperSerializers.ts +71 -0
  64. package/src/modular/buildOperations.ts +7 -1
  65. package/src/modular/buildSerializeUtils.ts +19 -11
  66. package/src/modular/buildSubpathIndex.ts +10 -0
  67. package/src/modular/emitModels.ts +66 -16
  68. package/src/modular/helpers/operationHelpers.ts +198 -84
  69. package/src/modular/helpers/typeHelpers.ts +24 -17
  70. package/src/modular/modularCodeModel.ts +2 -2
  71. package/src/modular/serialization/buildSerializerFunction.ts +224 -0
  72. package/src/transform/transformSchemas.ts +3 -0
  73. package/src/utils/importHelper.ts +64 -0
@@ -6,7 +6,11 @@ import {
6
6
  NameType,
7
7
  OperationResponse
8
8
  } from "@azure-tools/rlc-common";
9
- import { SdkContext } from "@azure-tools/typespec-client-generator-core";
9
+ import {
10
+ SdkContext,
11
+ SdkModelType,
12
+ SdkType
13
+ } from "@azure-tools/typespec-client-generator-core";
10
14
  import { NoTarget, Program } from "@typespec/compiler";
11
15
  import {
12
16
  FunctionDeclarationStructure,
@@ -14,7 +18,7 @@ import {
14
18
  ParameterDeclarationStructure
15
19
  } from "ts-morph";
16
20
  import { reportDiagnostic } from "../../lib.js";
17
- import { toPascalCase } from "../../utils/casingUtils.js";
21
+ import { toCamelCase, toPascalCase } from "../../utils/casingUtils.js";
18
22
  import {
19
23
  getCollectionFormatHelper,
20
24
  hasCollectionFormatInfo
@@ -40,7 +44,7 @@ import {
40
44
  getFixmeForMultilineDocs
41
45
  } from "./docsHelpers.js";
42
46
  import { getClassicalLayerPrefix, getOperationName } from "./namingHelpers.js";
43
- import { buildType } from "./typeHelpers.js";
47
+ import { buildType, isTypeNullable } from "./typeHelpers.js";
44
48
 
45
49
  function getRLCResponseType(rlcResponse?: OperationResponse) {
46
50
  if (!rlcResponse?.responses) {
@@ -217,7 +221,8 @@ export function getDeserializePrivateFunction(
217
221
  allParents.some((p) => p.type === "dict")) ||
218
222
  response.isBinaryPayload
219
223
  ) {
220
- statements.push(`return result.body`);
224
+ // TODO: Fix this any cast when implementing handling dict.
225
+ statements.push(`return result.body as any`);
221
226
  } else if (
222
227
  deserializedType &&
223
228
  properties.length > 0 &&
@@ -240,9 +245,7 @@ export function getDeserializePrivateFunction(
240
245
  deserializedType,
241
246
  deserializedRoot,
242
247
  runtimeImports,
243
- deserializedType.nullable !== undefined
244
- ? !deserializedType.nullable
245
- : false,
248
+ false, // TODO: Calculate if required
246
249
  [deserializedType],
247
250
  deserializedType.format
248
251
  )}`
@@ -563,7 +566,7 @@ function buildHeaderParameter(
563
566
  paramMap: string,
564
567
  param: Parameter
565
568
  ): string {
566
- if (!param.optional && param.type.nullable === true) {
569
+ if (!param.optional && isTypeNullable(param.type) === true) {
567
570
  reportDiagnostic(program, {
568
571
  code: "nullable-required-header",
569
572
  target: NoTarget
@@ -574,7 +577,7 @@ function buildHeaderParameter(
574
577
  if (param.optional) {
575
578
  conditions.push(`options?.${param.clientName} !== undefined`);
576
579
  }
577
- if (param.type.nullable === true) {
580
+ if (isTypeNullable(param.type) === true) {
578
581
  conditions.push(`options?.${param.clientName} !== null`);
579
582
  }
580
583
  return conditions.length > 0
@@ -596,7 +599,7 @@ function buildBodyParameter(
596
599
  !bodyParameter.type.aliasType &&
597
600
  !allParents.some((p) => p.type === "dict")
598
601
  ) {
599
- const bodyParts: string[] = getRequestModelMapping(
602
+ const { propertiesStr: bodyParts } = getRequestModelMapping(
600
603
  bodyParameter.type,
601
604
  bodyParameter.clientName,
602
605
  runtimeImports,
@@ -611,13 +614,32 @@ function buildBodyParameter(
611
614
  } else if (bodyParameter && bodyParts.length === 0) {
612
615
  return `\nbody: ${bodyParameter.clientName},`;
613
616
  }
617
+ } else if (isDiscriminatedUnion(bodyParameter.type.tcgcType)) {
618
+ const serializerName = toCamelCase(`${bodyParameter.type.name}Serializer`);
619
+ addImportToSpecifier("modularModel", runtimeImports, serializerName);
620
+ return `\nbody: ${serializerName}(${bodyParameter.clientName}),`;
614
621
  } else if (
615
622
  (bodyParameter.type.type === "model" &&
616
623
  (bodyParameter.type.aliasType ||
617
624
  allParents.some((p) => p.type === "dict"))) ||
618
625
  bodyParameter.type.type === "dict"
619
626
  ) {
620
- return `\nbody: ${bodyParameter.clientName},`;
627
+ const elementSerializerName =
628
+ bodyParameter.type.elementType?.type === "model"
629
+ ? toCamelCase(`${bodyParameter.type.elementType.name}Serializer`)
630
+ : "";
631
+ let modelSerializerName = "";
632
+
633
+ if (bodyParameter.type.type !== "dict") {
634
+ modelSerializerName = toCamelCase(`${bodyParameter.type.name}Serializer`);
635
+ }
636
+ if (modelSerializerName) {
637
+ return `\nbody: ${modelSerializerName}(${bodyParameter.clientName}),`;
638
+ } else {
639
+ // Need to do this so that Records are compatible with additional properties of other types
640
+ // this should check for compatibility mode once we support the additionalProperties property
641
+ return `\nbody: serializeRecord(${bodyParameter.clientName} as any, ${elementSerializerName}) as any,`;
642
+ }
621
643
  }
622
644
 
623
645
  if (bodyParameter.type.type === "list") {
@@ -625,15 +647,20 @@ function buildBodyParameter(
625
647
  bodyParameter.type.elementType?.type === "model" &&
626
648
  !bodyParameter.type.elementType.aliasType
627
649
  ) {
628
- const bodyParts = getRequestModelMapping(
629
- bodyParameter.type.elementType,
630
- "p",
631
- runtimeImports,
632
- [bodyParameter.type.elementType]
633
- );
634
- return `\nbody: (${bodyParameter.clientName} ?? []).map((p) => { return {
635
- ${bodyParts.join(", ")}
636
- };}),`;
650
+ const { propertiesStr: bodyParts, directAssignment } =
651
+ getRequestModelMapping(
652
+ bodyParameter.type.elementType,
653
+ "p",
654
+ runtimeImports,
655
+ [bodyParameter.type.elementType]
656
+ );
657
+ const mapBody =
658
+ directAssignment === true
659
+ ? bodyParts.join(", ")
660
+ : `{ ${bodyParts.join(", ")} }`;
661
+ return `\nbody: (${bodyParameter.clientName} ?? []).map((p) => {
662
+ return ${mapBody};
663
+ }),`;
637
664
  }
638
665
  return `\nbody: ${bodyParameter.clientName},`;
639
666
  }
@@ -764,12 +791,13 @@ function isRequired(param: Parameter | Property): param is RequiredType {
764
791
 
765
792
  function getRequired(param: RequiredType, runtimeImports: RuntimeImports) {
766
793
  if (param.type.type === "model") {
767
- return `"${param.restApiName}": {${getRequestModelMapping(
794
+ const { propertiesStr } = getRequestModelMapping(
768
795
  param.type,
769
796
  param.clientName,
770
797
  runtimeImports,
771
798
  [param.type]
772
- ).join(",")}}`;
799
+ );
800
+ return `"${param.restApiName}": { ${propertiesStr.join(",")} }`;
773
801
  }
774
802
  return `"${param.restApiName}": ${serializeRequestValue(
775
803
  param.type,
@@ -818,12 +846,13 @@ function isOptional(param: Parameter | Property): param is OptionalType {
818
846
 
819
847
  function getOptional(param: OptionalType, runtimeImports: RuntimeImports) {
820
848
  if (param.type.type === "model") {
821
- return `"${param.restApiName}": {${getRequestModelMapping(
849
+ const { propertiesStr } = getRequestModelMapping(
822
850
  param.type,
823
851
  "options?." + param.clientName + "?",
824
852
  runtimeImports,
825
853
  [param.type]
826
- ).join(", ")}}`;
854
+ );
855
+ return `"${param.restApiName}": { ${propertiesStr.join(", ")} }`;
827
856
  }
828
857
  if (
829
858
  param.restApiName === "api-version" &&
@@ -893,7 +922,7 @@ function getPathParameters(operation: Operation) {
893
922
  }
894
923
 
895
924
  function getNullableCheck(name: string, type: Type) {
896
- if (!type.nullable) {
925
+ if (!isTypeNullable(type)) {
897
926
  return "";
898
927
  }
899
928
 
@@ -905,33 +934,47 @@ function getNullableCheck(name: string, type: Type) {
905
934
  * This function helps translating an HLC request to RLC request,
906
935
  * extracting properties from body and headers and building the RLC response object
907
936
  */
937
+ interface RequestModelMappingResult {
938
+ propertiesStr: string[];
939
+ directAssignment?: boolean;
940
+ }
908
941
  export function getRequestModelMapping(
909
942
  modelPropertyType: Type,
910
943
  propertyPath: string = "body",
911
944
  runtimeImports: RuntimeImports,
912
945
  typeStack: Type[] = []
913
- ) {
946
+ ): RequestModelMappingResult {
914
947
  const props: string[] = [];
915
948
  const allParents = getAllAncestors(modelPropertyType);
916
949
  const properties: Property[] =
917
950
  getAllProperties(modelPropertyType, allParents) ?? [];
918
951
  if (properties.length <= 0) {
919
- return [];
952
+ return { propertiesStr: [] };
920
953
  }
954
+
955
+ let serializerName =
956
+ "name" in modelPropertyType && modelPropertyType.name
957
+ ? `${toCamelCase(modelPropertyType.name)}Serializer`
958
+ : undefined;
959
+
921
960
  if (isSpecialHandledUnion(modelPropertyType)) {
922
- const deserializeFunctionName = getDeserializeFunctionName(
923
- modelPropertyType,
924
- "serialize"
925
- );
926
- const definition = `${deserializeFunctionName}(${propertyPath})`;
961
+ serializerName =
962
+ serializerName ??
963
+ getDeserializeFunctionName(modelPropertyType, "serialize");
964
+ const definition = `${serializerName}(${propertyPath})`;
927
965
  props.push(definition);
928
- return props;
966
+ return { propertiesStr: props, directAssignment: true };
929
967
  }
930
968
  for (const property of properties) {
931
969
  if (property.readonly) {
932
970
  continue;
933
971
  }
934
- const propertyFullName = `${propertyPath}.${property.clientName}`;
972
+ const nullOrUndefinedPrefix = getPropertySerializationPrefix(
973
+ property,
974
+ propertyPath
975
+ );
976
+
977
+ const propertyFullName = getPropertyFullName(property, propertyPath);
935
978
  if (property.type.type === "model") {
936
979
  let definition;
937
980
  if (property.type.coreTypeInfo === "ErrorType") {
@@ -951,29 +994,35 @@ export function getRequestModelMapping(
951
994
  }`
952
995
  }`;
953
996
  } else if (isPolymorphicUnion(property.type)) {
954
- let nullOrUndefinedPrefix = "";
955
- if (property.optional || property.type.nullable) {
956
- nullOrUndefinedPrefix = `!${propertyFullName} ? ${propertyFullName} :`;
957
- }
958
- const deserializeFunctionName = getDeserializeFunctionName(
959
- property.type,
960
- "serialize"
961
- );
997
+ const deserializeFunctionName = property.type.name
998
+ ? `${toCamelCase(property.type.name)}Serializer`
999
+ : getDeserializeFunctionName(property.type, "serialize");
962
1000
  definition = `"${property.restApiName}": ${nullOrUndefinedPrefix}${deserializeFunctionName}(${propertyFullName})`;
963
1001
  } else {
964
- definition = `"${property.restApiName}": ${getNullableCheck(
965
- propertyFullName,
966
- property.type
967
- )} ${
968
- !property.optional ? "" : `!${propertyFullName} ? undefined :`
969
- } {${getRequestModelMapping(
970
- property.type,
971
- `${propertyPath}.${property.clientName}${
972
- property.optional ? "?" : ""
973
- }`,
974
- runtimeImports,
975
- [...typeStack, property.type]
976
- )}}`;
1002
+ if (property.type.name) {
1003
+ serializerName = `${toCamelCase(property.type.name)}Serializer`;
1004
+ definition = `"${property.restApiName}": ${nullOrUndefinedPrefix}${serializerName}(${propertyPath}.${property.clientName})`;
1005
+ } else {
1006
+ const { propertiesStr, directAssignment } = getRequestModelMapping(
1007
+ property.type,
1008
+ `${propertyPath}.${property.clientName}${
1009
+ property.optional ? "?" : ""
1010
+ }`,
1011
+ runtimeImports,
1012
+ [...typeStack, property.type]
1013
+ );
1014
+
1015
+ const serializeContent =
1016
+ directAssignment === true
1017
+ ? propertiesStr.join(",")
1018
+ : `{${propertiesStr.join(",")}}`;
1019
+ definition = `"${property.restApiName}": ${getNullableCheck(
1020
+ propertyFullName,
1021
+ property.type
1022
+ )} ${
1023
+ !property.optional ? "" : `!${propertyFullName} ? undefined :`
1024
+ } ${serializeContent}`;
1025
+ }
977
1026
  }
978
1027
 
979
1028
  props.push(definition);
@@ -987,6 +1036,23 @@ export function getRequestModelMapping(
987
1036
  }`
988
1037
  }`;
989
1038
  props.push(definition);
1039
+ } else if (property.type.type === "dict") {
1040
+ const modelName = property.type.elementType?.name;
1041
+ serializerName = modelName ? `${toCamelCase(modelName)}Serializer` : "";
1042
+ // definition = `"${property.restApiName}": ${nullOrUndefinedPrefix}${serializerName}(${propertyPath}.${property.clientName})`;
1043
+ const statement = `"${property.restApiName}": ${nullOrUndefinedPrefix} serializeRecord(${propertyFullName} as any, ${serializerName}) as any`;
1044
+ // addImportToSpecifier(
1045
+ // "serializerHelpers",
1046
+ // runtimeImports,
1047
+ // "serializeRecord"
1048
+ // );
1049
+
1050
+ addImportToSpecifier("modularModel", runtimeImports, serializerName);
1051
+ props.push(statement);
1052
+ } else if (modelPropertyType.type === "enum") {
1053
+ props.push(
1054
+ `"${property.restApiName}": ${nullOrUndefinedPrefix}${propertyPath}.${property.clientName}`
1055
+ );
990
1056
  } else {
991
1057
  const dot = propertyPath.endsWith("?") ? "." : "";
992
1058
  const clientValue = `${
@@ -1005,7 +1071,7 @@ export function getRequestModelMapping(
1005
1071
  }
1006
1072
  }
1007
1073
 
1008
- return props;
1074
+ return { propertiesStr: props };
1009
1075
  }
1010
1076
 
1011
1077
  /**
@@ -1046,10 +1112,10 @@ export function getResponseMapping(
1046
1112
  }`
1047
1113
  }`;
1048
1114
  } else if (isSpecialHandledUnion(property.type)) {
1049
- let nullOrUndefinedPrefix = "";
1050
- if (property.optional || property.type.nullable) {
1051
- nullOrUndefinedPrefix = `!${propertyFullName} ? ${propertyFullName} :`;
1052
- }
1115
+ const nullOrUndefinedPrefix = getPropertySerializationPrefix(
1116
+ property,
1117
+ propertyPath
1118
+ );
1053
1119
  const deserializeFunctionName = getDeserializeFunctionName(
1054
1120
  property.type,
1055
1121
  "deserialize"
@@ -1121,7 +1187,7 @@ export function deserializeResponseValue(
1121
1187
  format?: string
1122
1188
  ): string {
1123
1189
  const requiredPrefix = required === false ? `${restValue} === undefined` : "";
1124
- const nullablePrefix = type.nullable ? `${restValue} === null` : "";
1190
+ const nullablePrefix = isTypeNullable(type) ? `${restValue} === null` : "";
1125
1191
  const requiredOrNullablePrefix =
1126
1192
  requiredPrefix !== "" && nullablePrefix !== ""
1127
1193
  ? `(${requiredPrefix} || ${nullablePrefix})`
@@ -1129,13 +1195,13 @@ export function deserializeResponseValue(
1129
1195
  switch (type.type) {
1130
1196
  case "datetime":
1131
1197
  return required
1132
- ? type.nullable
1198
+ ? isTypeNullable(type)
1133
1199
  ? `${restValue} === null ? null : new Date(${restValue})`
1134
1200
  : `new Date(${restValue})`
1135
1201
  : `${restValue} !== undefined? new Date(${restValue}): undefined`;
1136
1202
  case "list": {
1137
1203
  const prefix =
1138
- required && !type.nullable
1204
+ required && !isTypeNullable(type)
1139
1205
  ? `${restValue}`
1140
1206
  : `${requiredOrNullablePrefix} ? ${restValue} : ${restValue}`;
1141
1207
  if (type.elementType?.type === "model") {
@@ -1148,7 +1214,7 @@ export function deserializeResponseValue(
1148
1214
  )}}))`;
1149
1215
  } else if (isPolymorphicUnion(type.elementType)) {
1150
1216
  let nullOrUndefinedPrefix = "";
1151
- if (type.elementType.nullable) {
1217
+ if (isTypeNullable(type.elementType)) {
1152
1218
  nullOrUndefinedPrefix = `!p ? p :`;
1153
1219
  }
1154
1220
  const deserializeFunctionName = getDeserializeFunctionName(
@@ -1233,7 +1299,7 @@ export function serializeRequestValue(
1233
1299
  ): string {
1234
1300
  const requiredPrefix =
1235
1301
  required === false ? `${clientValue} === undefined` : "";
1236
- const nullablePrefix = type.nullable ? `${clientValue} === null` : "";
1302
+ const nullablePrefix = isTypeNullable(type) ? `${clientValue} === null` : "";
1237
1303
  const requiredOrNullablePrefix =
1238
1304
  requiredPrefix !== "" && nullablePrefix !== ""
1239
1305
  ? `(${requiredPrefix} || ${nullablePrefix})`
@@ -1252,20 +1318,32 @@ export function serializeRequestValue(
1252
1318
  return `${clientValue}${required ? "" : "?"}.getTime()`;
1253
1319
  case "rfc3339":
1254
1320
  default:
1255
- return `${clientValue}${required ? "" : "?"}.toISOString()`;
1321
+ return `${getNullableCheck(clientValue, type)} ${clientValue}${
1322
+ required ? "" : "?"
1323
+ }.toISOString()`;
1256
1324
  }
1257
1325
  case "list": {
1258
1326
  const prefix =
1259
- required && !type.nullable
1327
+ required && !isTypeNullable(type)
1260
1328
  ? `${clientValue}`
1261
1329
  : `${requiredOrNullablePrefix}? ${clientValue}: ${clientValue}`;
1262
1330
  if (type.elementType?.type === "model" && !type.elementType.aliasType) {
1263
- return `${prefix}.map(p => ({${getRequestModelMapping(
1264
- type.elementType,
1265
- "p",
1266
- runtimeImports,
1267
- [...typeStack, type.elementType]
1268
- )}}))`;
1331
+ if (!type.elementType.name) {
1332
+ // If it is an anonymous model we need to serialize inline
1333
+ const { propertiesStr } = getRequestModelMapping(
1334
+ type.elementType,
1335
+ "p",
1336
+ runtimeImports,
1337
+ [...typeStack, type.elementType]
1338
+ );
1339
+
1340
+ return `${prefix}.map(p => ({${propertiesStr}}))`;
1341
+ } else {
1342
+ // When it is not anonymous we can hand it off to the serializer function
1343
+ return `${prefix}.map(${toCamelCase(
1344
+ type.elementType.name + "Serializer"
1345
+ )})`;
1346
+ }
1269
1347
  } else if (
1270
1348
  needsDeserialize(type.elementType) &&
1271
1349
  !type.elementType?.aliasType
@@ -1274,7 +1352,7 @@ export function serializeRequestValue(
1274
1352
  type.elementType!,
1275
1353
  "p",
1276
1354
  runtimeImports,
1277
- required,
1355
+ true,
1278
1356
  [...typeStack, type.elementType!],
1279
1357
  type.elementType?.format
1280
1358
  )})`;
@@ -1283,13 +1361,12 @@ export function serializeRequestValue(
1283
1361
  isPolymorphicUnion(type.elementType)
1284
1362
  ) {
1285
1363
  let nullOrUndefinedPrefix = "";
1286
- if (type.elementType.nullable) {
1364
+ if (isTypeNullable(type.elementType)) {
1287
1365
  nullOrUndefinedPrefix = `!p ? p :`;
1288
1366
  }
1289
- const serializeFunctionName = getDeserializeFunctionName(
1290
- type.elementType,
1291
- "serialize"
1292
- );
1367
+ const serializeFunctionName = type.elementType?.name
1368
+ ? `${toCamelCase(type.elementType.name)}Serializer`
1369
+ : getDeserializeFunctionName(type.elementType, "serialize");
1293
1370
  return `${prefix}.map(p => ${nullOrUndefinedPrefix}${serializeFunctionName}(p))`;
1294
1371
  } else {
1295
1372
  return clientValue;
@@ -1299,7 +1376,10 @@ export function serializeRequestValue(
1299
1376
  if (format !== "binary") {
1300
1377
  addImportToSpecifier("coreUtil", runtimeImports, "uint8ArrayToString");
1301
1378
  return required
1302
- ? `uint8ArrayToString(${clientValue}, "${
1379
+ ? `${getNullableCheck(
1380
+ clientValue,
1381
+ type
1382
+ )} uint8ArrayToString(${clientValue}, "${
1303
1383
  getEncodingFormat({ format }) ?? "base64"
1304
1384
  }")`
1305
1385
  : `${clientValue} !== undefined ? uint8ArrayToString(${clientValue}, "${
@@ -1311,10 +1391,9 @@ export function serializeRequestValue(
1311
1391
  if (isNormalUnion(type)) {
1312
1392
  return `${clientValue}`;
1313
1393
  } else if (isSpecialHandledUnion(type)) {
1314
- const serializeFunctionName = getDeserializeFunctionName(
1315
- type,
1316
- "serialize"
1317
- );
1394
+ const serializeFunctionName = type.name
1395
+ ? `${toCamelCase(type.name)}Serializer`
1396
+ : getDeserializeFunctionName(type, "serialize");
1318
1397
  return `${serializeFunctionName}(${clientValue})`;
1319
1398
  } else {
1320
1399
  return `${clientValue} as any`;
@@ -1399,3 +1478,38 @@ export function getAllAncestors(type: Type): Type[] {
1399
1478
  });
1400
1479
  return ancestors;
1401
1480
  }
1481
+
1482
+ export function getPropertySerializationPrefix(
1483
+ modularType: Property | Parameter,
1484
+ propertyPath?: string
1485
+ ) {
1486
+ const propertyFullName = getPropertyFullName(modularType, propertyPath);
1487
+ if (modularType.optional || isTypeNullable(modularType.type)) {
1488
+ return `!${propertyFullName} ? ${propertyFullName} :`;
1489
+ }
1490
+
1491
+ return "";
1492
+ }
1493
+
1494
+ export function getPropertyFullName(
1495
+ modularType: Property | Parameter,
1496
+ propertyPath?: string
1497
+ ) {
1498
+ let fullName = `${modularType.clientName}`;
1499
+ if (propertyPath) {
1500
+ fullName = `${propertyPath}.${modularType.clientName}`;
1501
+ }
1502
+ return fullName;
1503
+ }
1504
+
1505
+ export function isDiscriminatedUnion(type?: SdkType): type is SdkModelType {
1506
+ if (!type) {
1507
+ return false;
1508
+ }
1509
+
1510
+ return Boolean(
1511
+ type.kind === "model" &&
1512
+ type.discriminatorProperty &&
1513
+ type.discriminatedSubtypes
1514
+ );
1515
+ }
@@ -72,7 +72,7 @@ export function getType(type: Type, format?: string): TypeMetadata {
72
72
  const simpleType = simpleTypeMap[type.type];
73
73
  if (simpleType) {
74
74
  const typeMetadata: TypeMetadata = { ...simpleType };
75
- if (type.nullable) {
75
+ if (isTypeNullable(type)) {
76
76
  typeMetadata.nullable = true;
77
77
  typeMetadata.name = handleNullableTypeName(typeMetadata);
78
78
  }
@@ -102,7 +102,7 @@ export function getType(type: Type, format?: string): TypeMetadata {
102
102
  return handleDictType(type);
103
103
 
104
104
  case "never":
105
- return { name: "never", nullable: type.nullable };
105
+ return { name: "never", nullable: isTypeNullable(type) };
106
106
  default:
107
107
  throw new Error(`Unsupported type ${type.type}`);
108
108
  }
@@ -118,9 +118,9 @@ function handleConstantType(type: Type): TypeMetadata {
118
118
  }
119
119
  const name = handleNullableTypeName({
120
120
  name: typeName,
121
- nullable: type.nullable
121
+ nullable: isTypeNullable(type)
122
122
  });
123
- return { name, nullable: type.nullable };
123
+ return { name, nullable: isTypeNullable(type) };
124
124
  }
125
125
 
126
126
  /**
@@ -137,20 +137,24 @@ function handleEnumType(type: Type): TypeMetadata {
137
137
  return getType(e as Type).name;
138
138
  })
139
139
  .join(" | ")}`,
140
- nullable: type.nullable
140
+ nullable: isTypeNullable(type)
141
141
  };
142
142
  }
143
143
  const name = handleNullableTypeName({
144
144
  name: type.name,
145
- nullable: type.nullable
145
+ nullable: isTypeNullable(type)
146
146
  });
147
147
  return {
148
148
  name,
149
- nullable: type.nullable,
149
+ nullable: isTypeNullable(type),
150
150
  originModule: "models.js"
151
151
  };
152
152
  }
153
153
 
154
+ export function isTypeNullable(type: Type) {
155
+ return Boolean(type.tcgcType?.kind === "nullable");
156
+ }
157
+
154
158
  /**
155
159
  * Handles the conversion of list types to TypeScript representation metadata.
156
160
  */
@@ -164,14 +168,17 @@ function handleListType(type: Type): TypeMetadata {
164
168
  type.elementType.format
165
169
  );
166
170
 
167
- const name = handleNullableTypeName({
168
- name: `${elementTypeMetadata.name}[]`,
169
- nullable: type.nullable
170
- });
171
+ let name = `${elementTypeMetadata.name}[]`;
172
+
173
+ const isTypeNullable = type.tcgcType?.kind === "nullable";
174
+
175
+ if (isTypeNullable) {
176
+ name = `(${name} | null)`;
177
+ }
171
178
 
172
179
  const listTypeMetadata: TypeMetadata = {
173
180
  name: name,
174
- nullable: type.elementType.nullable,
181
+ nullable: isTypeNullable,
175
182
  originModule: type.elementType?.type === "model" ? "models.js" : undefined
176
183
  };
177
184
 
@@ -185,11 +192,11 @@ function handleModelType(type: Type): TypeMetadata {
185
192
  let name = !type.name ? handleAnomymousModelName(type) : type.name;
186
193
  name = handleNullableTypeName({
187
194
  name,
188
- nullable: type.nullable
195
+ nullable: isTypeNullable(type)
189
196
  });
190
197
  return {
191
198
  name,
192
- nullable: type.nullable,
199
+ nullable: isTypeNullable(type),
193
200
  originModule: "models.js"
194
201
  };
195
202
  }
@@ -200,10 +207,10 @@ function handleModelType(type: Type): TypeMetadata {
200
207
  function handleDurationType(type: Type, format?: string): TypeMetadata {
201
208
  const isFormatSeconds = format === "seconds";
202
209
  let name = isFormatSeconds ? "number" : "string";
203
- name = handleNullableTypeName({ name, nullable: type.nullable });
210
+ name = handleNullableTypeName({ name, nullable: isTypeNullable(type) });
204
211
  return {
205
212
  name,
206
- nullable: type.nullable
213
+ nullable: isTypeNullable(type)
207
214
  };
208
215
  }
209
216
 
@@ -222,7 +229,7 @@ function handleCombinedType(type: Type): TypeMetadata {
222
229
  return `${sdkType}`;
223
230
  })
224
231
  .join(" | ");
225
- return { name: `(${name})`, nullable: type.nullable };
232
+ return { name: `(${name})`, nullable: isTypeNullable(type) };
226
233
  }
227
234
 
228
235
  /**
@@ -8,7 +8,7 @@ import {
8
8
  SdkBodyParameter,
9
9
  SdkType
10
10
  } from "@azure-tools/typespec-client-generator-core";
11
- import { UsageFlags } from "@typespec/compiler";
11
+ import { UsageFlags, Type as TypespecType } from "@typespec/compiler";
12
12
  import { Project } from "ts-morph";
13
13
 
14
14
  export interface ModularOptions {
@@ -75,7 +75,6 @@ export interface EnumValue {
75
75
  description: string;
76
76
  }
77
77
  export interface Type {
78
- nullable?: boolean;
79
78
  name?: string;
80
79
  description?: string;
81
80
  type:
@@ -120,6 +119,7 @@ export interface Type {
120
119
  discriminatorValue?: string;
121
120
  isPolymorphicBaseModel?: boolean;
122
121
  tcgcType?: SdkType;
122
+ __raw?: TypespecType;
123
123
  }
124
124
 
125
125
  export interface Client {