@azure-tools/typespec-ts 0.51.1 → 0.52.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.
@@ -22,6 +22,7 @@ import { getClientOptions, isHttpMetadata, isReadOnly } from "@azure-tools/types
22
22
  import { isHeader, isMetadata } from "@typespec/http";
23
23
  import { useContext } from "../../contextManager.js";
24
24
  import { getHeaderClientOptions, getRestErrorCodeHeader } from "./clientOptionHelpers.js";
25
+ import { getClientParameterName } from "./clientHelpers.js";
25
26
  import { isExtensibleEnum } from "../type-expressions/get-enum-expression.js";
26
27
  import { emitInlineModel } from "../type-expressions/get-model-expression.js";
27
28
  /**
@@ -96,7 +97,16 @@ export function getDeserializePrivateFunction(context, method) {
96
97
  const restResponse = operation.operation.responses[0];
97
98
  let returnType;
98
99
  if (isLroOnly || isLroAndPaging) {
99
- returnType = buildLroReturnType(context, operation);
100
+ if (isLroOnly && shouldWrap) {
101
+ // For LRO-only operations with non-model final result, wrap in a response type alias
102
+ returnType = {
103
+ name: getOperationResponseTypeName(method),
104
+ type: resolveReference(refkey(operation, "response"))
105
+ };
106
+ }
107
+ else {
108
+ returnType = buildLroReturnType(context, operation);
109
+ }
100
110
  }
101
111
  else if (isPagingOnly && (restResponse === null || restResponse === void 0 ? void 0 : restResponse.type)) {
102
112
  // For paging operations, use the full response model (e.g., _OperationListResult)
@@ -237,7 +247,7 @@ export function getDeserializePrivateFunction(context, method) {
237
247
  nameOnly: true,
238
248
  skipDiscriminatedUnionSuffix: false
239
249
  });
240
- // Handle wrap-non-model-return for non-LRO, non-paging operations
250
+ // Handle wrap-non-model-return for non-LRO, non-paging and LRO-only operations
241
251
  if (shouldWrap) {
242
252
  if (isBinary) {
243
253
  // Binary wrap: getBinaryStream already resolved the stream,
@@ -247,8 +257,9 @@ export function getDeserializePrivateFunction(context, method) {
247
257
  }
248
258
  else {
249
259
  // Non-model response: wrap with body property
250
- // Generate the appropriate deserialization for the body value
251
- const bodyValue = deserializeResponseValue(context, deserializedType, "result.body", true, getEncodeForType(deserializedType));
260
+ // Generate the appropriate deserialization for the body value.
261
+ // For LRO operations, deserializedRoot may include the sub-path (e.g. result.body.someProperty).
262
+ const bodyValue = deserializeResponseValue(context, deserializedType, deserializedRoot, true, getEncodeForType(deserializedType));
252
263
  statements.push(`return { body: ${bodyValue} };`);
253
264
  }
254
265
  return {
@@ -829,6 +840,15 @@ function getLroOnlyOperationFunction(context, method, clientType, optionalParamN
829
840
  const { name, fixme = [] } = getOperationName(operation);
830
841
  const pollerLikeReference = resolveReference(AzurePollingDependencies.PollerLike);
831
842
  const operationStateReference = resolveReference(AzurePollingDependencies.OperationState);
843
+ // When wrap-non-model-return is enabled and the LRO final result is a non-model type,
844
+ // use the wrapper response type (e.g. GetIkeSasResponse) instead of the raw type (e.g. string).
845
+ const { shouldWrap } = checkWrapNonModelReturn(context, operation);
846
+ const effectiveReturnTypeStr = shouldWrap
847
+ ? resolveReference(refkey(operation, "response"))
848
+ : returnType.type;
849
+ const effectiveReturnTypeName = shouldWrap
850
+ ? getOperationResponseTypeName(method)
851
+ : returnType.type;
832
852
  const functionStatement = {
833
853
  kind: StructureKind.Function,
834
854
  docs: [
@@ -840,9 +860,9 @@ function getLroOnlyOperationFunction(context, method, clientType, optionalParamN
840
860
  name,
841
861
  propertyName: normalizeName(operation.name, NameType.Property),
842
862
  isLro: true,
843
- lroFinalReturnType: returnType.type,
863
+ lroFinalReturnType: effectiveReturnTypeName,
844
864
  parameters,
845
- returnType: `${pollerLikeReference}<${operationStateReference}<${returnType.type}>, ${returnType.type}>`
865
+ returnType: `${pollerLikeReference}<${operationStateReference}<${effectiveReturnTypeStr}>, ${effectiveReturnTypeStr}>`
846
866
  };
847
867
  const getLongRunningPollerReference = resolveReference(PollingHelpers.GetLongRunningPoller);
848
868
  const lroMetadata = operation.kind === "lro" || operation.kind === "lropaging"
@@ -870,7 +890,7 @@ function getLroOnlyOperationFunction(context, method, clientType, optionalParamN
870
890
  .join(", ")}),
871
891
  ${resourceLocationConfig}
872
892
  ${apiVersion ? `apiVersion: ${apiVersion}` : ""}
873
- }) as ${pollerLikeReference}<${operationStateReference}<${returnType.type}>, ${returnType.type}>;
893
+ }) as ${pollerLikeReference}<${operationStateReference}<${effectiveReturnTypeStr}>, ${effectiveReturnTypeStr}>;
874
894
  `);
875
895
  return {
876
896
  ...functionStatement,
@@ -1082,9 +1102,11 @@ function getHeaderAndBodyParameters(dpgContext, operation, optionalParamName = "
1082
1102
  // Check if this parameter still exists in the corresponding method params (after override)
1083
1103
  if (param.methodParameterSegments &&
1084
1104
  param.methodParameterSegments.length > 0) {
1105
+ const paramAccessor = getParamAccessor(param, optionalParamName);
1085
1106
  parametersImplementation[param.kind].push({
1086
- paramMap: getParameterMap(dpgContext, param, optionalParamName),
1087
- param
1107
+ paramMap: getParameterMap(dpgContext, param, paramAccessor),
1108
+ param,
1109
+ paramAccessor
1088
1110
  });
1089
1111
  }
1090
1112
  }
@@ -1095,7 +1117,7 @@ function getHeaderAndBodyParameters(dpgContext, operation, optionalParamName = "
1095
1117
  }
1096
1118
  if (parametersImplementation.header.length) {
1097
1119
  paramStr = `${paramStr}\nheaders: {${parametersImplementation.header
1098
- .map((i) => buildHeaderParameter(dpgContext.program, i.paramMap, i.param, optionalParamName))
1120
+ .map((i) => buildHeaderParameter(dpgContext.program, i.paramMap, i.param, i.paramAccessor))
1099
1121
  .join(",\n")}, ...${optionalParamName}.requestOptions?.headers },`;
1100
1122
  }
1101
1123
  if (operation.operation.bodyParam === undefined &&
@@ -1110,8 +1132,7 @@ function getHeaderAndBodyParameters(dpgContext, operation, optionalParamName = "
1110
1132
  return paramStr;
1111
1133
  }
1112
1134
  // Specially handle the type for headers because we only allow string/number/boolean values
1113
- function buildHeaderParameter(program, paramMap, param, optionalParamName = "options") {
1114
- const paramName = param.name;
1135
+ function buildHeaderParameter(program, paramMap, param, paramAccessor) {
1115
1136
  const effectiveOptional = getEffectiveOptional(param);
1116
1137
  if (!effectiveOptional && isTypeNullable(param.type) === true) {
1117
1138
  reportDiagnostic(program, {
@@ -1122,10 +1143,10 @@ function buildHeaderParameter(program, paramMap, param, optionalParamName = "opt
1122
1143
  }
1123
1144
  const conditions = [];
1124
1145
  if (effectiveOptional) {
1125
- conditions.push(`${optionalParamName}?.${paramName} !== undefined`);
1146
+ conditions.push(`${paramAccessor} !== undefined`);
1126
1147
  }
1127
1148
  if (isTypeNullable(param.type) === true) {
1128
- conditions.push(`${optionalParamName}?.${paramName} !== null`);
1149
+ conditions.push(`${paramAccessor} !== null`);
1129
1150
  }
1130
1151
  return conditions.length > 0
1131
1152
  ? `...(${conditions.join(" && ")} ? {${paramMap}} : {})`
@@ -1214,7 +1235,7 @@ function getEncodingFormat(type) {
1214
1235
  /**
1215
1236
  * This function helps with renames, translating client names to rest api names
1216
1237
  */
1217
- export function getParameterMap(context, param, optionalParamName = "options") {
1238
+ export function getParameterMap(context, param, paramAccessor) {
1218
1239
  var _a;
1219
1240
  // Use lowercase for header names since HTTP headers are case-insensitive
1220
1241
  const serializedName = param.kind === "header"
@@ -1232,14 +1253,14 @@ export function getParameterMap(context, param, optionalParamName = "options") {
1232
1253
  return `"${serializedName}": ${param.onClient ? "context." : ""}${param.name} ?? "${param.clientDefaultValue}"`;
1233
1254
  }
1234
1255
  if (hasCollectionFormatInfo(param.kind, param.collectionFormat)) {
1235
- return getCollectionFormatForParam(context, param, optionalParamName, serializedName);
1256
+ return getCollectionFormatForParam(context, param, paramAccessor, serializedName);
1236
1257
  }
1237
1258
  // if the parameter or property is optional, we don't need to handle the default value
1238
1259
  if (isOptional(param)) {
1239
- return getOptional(context, param, optionalParamName, serializedName);
1260
+ return getOptional(context, param, serializedName, paramAccessor);
1240
1261
  }
1241
1262
  if (isRequired(param)) {
1242
- return getRequired(context, param, serializedName);
1263
+ return getRequired(context, param, serializedName, paramAccessor);
1243
1264
  }
1244
1265
  reportDiagnostic(context.program, {
1245
1266
  code: "unsupported-parameter-type",
@@ -1252,9 +1273,9 @@ export function getParameterMap(context, param, optionalParamName = "options") {
1252
1273
  // Return a fallback value to allow the emitter to continue
1253
1274
  return `"${param.name}": undefined`;
1254
1275
  }
1255
- function getCollectionFormatForParam(context, param, optionalParamName = "options", serializedName) {
1276
+ function getCollectionFormatForParam(context, param, paramAccessor, serializedName) {
1256
1277
  const format = param.collectionFormat;
1257
- return `"${serializedName}": ${serializeRequestValue(context, param.type, param.optional ? `${optionalParamName}?.${param.name}` : param.name, !param.optional, format, serializedName, true)}`;
1278
+ return `"${serializedName}": ${serializeRequestValue(context, param.type, paramAccessor, !param.optional, format, serializedName, true)}`;
1258
1279
  }
1259
1280
  function isContentType(param) {
1260
1281
  return (param.kind === "header" &&
@@ -1302,13 +1323,12 @@ function getEffectiveOptional(param) {
1302
1323
  function isRequired(param) {
1303
1324
  return !getEffectiveOptional(param);
1304
1325
  }
1305
- function getRequired(context, param, serializedName) {
1306
- const clientValue = `${param.onClient ? "context." : ""}${param.name}`;
1326
+ function getRequired(context, param, serializedName, paramAccessor) {
1307
1327
  if (param.type.kind === "model") {
1308
- const propertiesStr = getRequestModelMapping(context, { ...param.type, optional: param.optional }, clientValue);
1328
+ const propertiesStr = getRequestModelMapping(context, { ...param.type, optional: param.optional }, paramAccessor);
1309
1329
  return `"${serializedName}": { ${propertiesStr.join(",")} }`;
1310
1330
  }
1311
- return `"${serializedName}": ${serializeRequestValue(context, param.type, clientValue, true, getEncodeForType(param.type), serializedName, true)}`;
1331
+ return `"${serializedName}": ${serializeRequestValue(context, param.type, paramAccessor, true, getEncodeForType(param.type), serializedName, true)}`;
1312
1332
  }
1313
1333
  function getConstantValue(param) {
1314
1334
  if (typeof param.value === "string") {
@@ -1322,19 +1342,18 @@ function isConstant(param) {
1322
1342
  function isOptional(param) {
1323
1343
  return getEffectiveOptional(param);
1324
1344
  }
1325
- function getOptional(context, param, optionalParamName, serializedName) {
1326
- const paramName = `${param.onClient ? "context." : `${optionalParamName}?.`}${param.name}`;
1345
+ function getOptional(context, param, serializedName, paramAccessor) {
1327
1346
  // Apply client default value if present and type matches
1328
1347
  const defaultSuffix = param.clientDefaultValue !== undefined &&
1329
1348
  isDefaultValueTypeMatch(param, param.clientDefaultValue)
1330
1349
  ? ` ?? ${formatDefaultValue(param.clientDefaultValue)}`
1331
1350
  : "";
1332
1351
  if (param.type.kind === "model") {
1333
- const propertiesStr = getRequestModelMapping(context, { ...param.type, optional: param.optional }, paramName + "?.");
1352
+ const propertiesStr = getRequestModelMapping(context, { ...param.type, optional: param.optional }, paramAccessor + "?.");
1334
1353
  const serializeContent = `{${propertiesStr.join(",")}}`;
1335
1354
  return `"${serializedName}": ${serializeContent}`;
1336
1355
  }
1337
- const serializedValue = serializeRequestValue(context, param.type, paramName, false, getEncodeForType(param.type), serializedName, true);
1356
+ const serializedValue = serializeRequestValue(context, param.type, paramAccessor, false, getEncodeForType(param.type), serializedName, true);
1338
1357
  return `"${serializedName}": ${serializedValue}${defaultSuffix}`;
1339
1358
  }
1340
1359
  /**
@@ -1368,7 +1387,7 @@ function getPathParameters(operation, optionalParamName = "options") {
1368
1387
  if (param.kind === "path") {
1369
1388
  const methodParam = (_a = param.methodParameterSegments[0]) === null || _a === void 0 ? void 0 : _a[0];
1370
1389
  if (methodParam) {
1371
- pathParams.push(`"${param.serializedName}": ${getPathParamExpr(methodParam, getDefaultValue(param), optionalParamName)}`);
1390
+ pathParams.push(`"${param.serializedName}": ${getPathParamExpr(param, getDefaultValue(param), optionalParamName)}`);
1372
1391
  }
1373
1392
  }
1374
1393
  }
@@ -1390,13 +1409,14 @@ function getQueryParameters(dpgContext, operation) {
1390
1409
  // Check if this parameter still exists in the corresponding method params (after override)
1391
1410
  if (param.methodParameterSegments &&
1392
1411
  param.methodParameterSegments.length > 0) {
1412
+ const paramAccessor = getParamAccessor(param);
1393
1413
  parametersImplementation[param.kind].push({
1394
1414
  paramMap: getParameterMap(dpgContext, {
1395
1415
  ...param,
1396
1416
  // TODO: remember to remove this hack once compiler gives us a name
1397
1417
  // https://github.com/microsoft/typespec/issues/6743
1398
1418
  serializedName: getUriTemplateQueryParamName(param.serializedName)
1399
- }),
1419
+ }, paramAccessor),
1400
1420
  param
1401
1421
  });
1402
1422
  }
@@ -1413,15 +1433,69 @@ function escapeUriTemplateParamName(name) {
1413
1433
  return "%" + c.charCodeAt(0).toString(16).toUpperCase();
1414
1434
  });
1415
1435
  }
1436
+ /**
1437
+ * Returns the parameter expression matching the operation signature for an HTTP parameter.
1438
+ * 1. Client-level parameter (`param.onClient`): returns `context.<paramName>`.
1439
+ * 2. Method-level parameter with `methodParameterSegments`: returns the expression
1440
+ * built from those segments (e.g. `options?.ocpDate`, `body.nested`).
1441
+ * 3. Fallback: returns the parameter name directly, with optional chaining if optional.
1442
+ */
1443
+ function getParamAccessor(param, optionalParamName = "options") {
1444
+ const methodParamExpr = getMethodParamExpr(param, optionalParamName);
1445
+ const clientPrefix = "context.";
1446
+ if (methodParamExpr) {
1447
+ return param.onClient
1448
+ ? `${clientPrefix}${methodParamExpr}`
1449
+ : methodParamExpr;
1450
+ }
1451
+ if (param.onClient) {
1452
+ return `${clientPrefix}${getClientParameterName(param)}`;
1453
+ }
1454
+ if (getEffectiveOptional(param)) {
1455
+ return `${optionalParamName}?.${param.name}`;
1456
+ }
1457
+ return param.name;
1458
+ }
1459
+ /**
1460
+ * Builds a property accessor expression from the param's `methodParameterSegments`.
1461
+ * Each segment represents a level of property access (e.g. `options?.nested.value`).
1462
+ * Returns `undefined` when no segments are available, so the caller can fall back.
1463
+ */
1464
+ function getMethodParamExpr(param, optionalParamName = "options") {
1465
+ const segments = param.methodParameterSegments;
1466
+ if (segments.length === 0) {
1467
+ return undefined;
1468
+ }
1469
+ const path = segments[0];
1470
+ if (!path || path.length < 1) {
1471
+ return undefined;
1472
+ }
1473
+ const parts = [];
1474
+ for (let i = 0; i < path.length; i++) {
1475
+ const segment = path[i];
1476
+ if (i === 0) {
1477
+ // Normalize names for client-level segments to match the context interface property names
1478
+ const segmentName = segment.onClient
1479
+ ? getClientParameterName(segment)
1480
+ : segment.name;
1481
+ if (segment.optional && !segment.onClient) {
1482
+ // If the first segment is optional and not on the client, we need to start with the optionalParamName
1483
+ parts.push(`${optionalParamName}?.`);
1484
+ }
1485
+ parts.push(segmentName);
1486
+ }
1487
+ else {
1488
+ const needsOptionalChain = path[i - 1].optional;
1489
+ parts.push(`${needsOptionalChain ? "?." : "."}${segment.name}`);
1490
+ }
1491
+ }
1492
+ return parts.join("");
1493
+ }
1416
1494
  function getPathParamExpr(param, defaultValue, optionalParamName = "options") {
1417
1495
  if (isConstant(param.type)) {
1418
1496
  return getConstantValue(param.type);
1419
1497
  }
1420
- const paramName = param.onClient
1421
- ? `context.${param.name}`
1422
- : param.optional
1423
- ? `${optionalParamName}["${param.name}"]`
1424
- : param.name;
1498
+ const paramName = getParamAccessor(param, optionalParamName);
1425
1499
  return defaultValue
1426
1500
  ? typeof defaultValue === "string"
1427
1501
  ? `${paramName} ?? "${defaultValue}"`
@@ -2132,67 +2206,71 @@ export function getOperationResponseTypeName(method) {
2132
2206
  : "";
2133
2207
  return `${prefix}${normalizeName(operation.name, NameType.Interface)}Response`;
2134
2208
  }
2209
+ /**
2210
+ * Returns true when a type should be wrapped with a `body` property.
2211
+ * Wrapping is needed for primitive/enum types; composite (model, dict, model-array)
2212
+ * and unknown-as-record types map to the HLC PropertyKind.Composite / Dictionary
2213
+ * patterns which do NOT get a body wrapper.
2214
+ *
2215
+ * Covered cases (no wrap):
2216
+ * - model array (e.g. Foo[]) → HLC PropertyKind.Composite
2217
+ * - model → HLC PropertyKind.Composite
2218
+ * - dict / Record<string, unknown> → HLC PropertyKind.Dictionary
2219
+ * - unknown with treatUnknownAsRecord → treated as Dict
2220
+ *
2221
+ * Covered cases (wrap):
2222
+ * - string, boolean, number → HLC PropertyKind.Primitive
2223
+ * - string[] → HLC PropertyKind.Primitive (item kind)
2224
+ * - enum / KnownXxx | string → HLC PropertyKind.Enum
2225
+ * - any / unknown (no treatAsRecord) → HLC PropertyKind.Primitive
2226
+ */
2227
+ function isWrappableType(context, type) {
2228
+ var _a;
2229
+ if (type.kind === "array" && type.valueType.kind === "model")
2230
+ return false;
2231
+ if (type.kind === "dict" || type.kind === "model")
2232
+ return false;
2233
+ if (type.kind === "unknown" && ((_a = context.rlcOptions) === null || _a === void 0 ? void 0 : _a.treatUnknownAsRecord))
2234
+ return false;
2235
+ return true;
2236
+ }
2135
2237
  /**
2136
2238
  * Determines whether wrapping the non-model return type is needed for an operation.
2137
2239
  * Returns an object with `shouldWrap` (whether to wrap) and `isBinary` (whether it's a binary response).
2138
2240
  */
2139
2241
  export function checkWrapNonModelReturn(context, operation) {
2140
- var _a, _b, _c, _d;
2242
+ var _a, _b, _c, _d, _e;
2141
2243
  const noWrap = { shouldWrap: false, isBinary: false };
2142
- // Only for non-LRO, non-paging normal operations
2143
- if (isLroOnlyOperation(operation) ||
2144
- isLroAndPagingOperation(operation) ||
2145
- isPagingOnlyOperation(operation)) {
2244
+ // LRO+paging and paging-only operations are not wrapped
2245
+ if (isLroAndPagingOperation(operation) || isPagingOnlyOperation(operation)) {
2146
2246
  return noWrap;
2147
2247
  }
2148
2248
  // Only if the feature flag is enabled
2149
2249
  if (!((_a = context.rlcOptions) === null || _a === void 0 ? void 0 : _a.wrapNonModelReturn)) {
2150
2250
  return noWrap;
2151
2251
  }
2152
- const response = operation.response;
2153
- if (!response.type) {
2252
+ // For LRO-only operations, check the final result type from LRO metadata
2253
+ if (isLroOnlyOperation(operation)) {
2254
+ const lroResultType = (_c = (_b = operation.lroMetadata) === null || _b === void 0 ? void 0 : _b.finalResponse) === null || _c === void 0 ? void 0 : _c.result;
2255
+ if (!lroResultType) {
2256
+ return noWrap; // void LRO - no wrap needed
2257
+ }
2258
+ return {
2259
+ shouldWrap: isWrappableType(context, lroResultType),
2260
+ isBinary: false
2261
+ };
2262
+ }
2263
+ const { type } = operation.response;
2264
+ if (!type) {
2154
2265
  return noWrap; // void return type - no wrap needed
2155
2266
  }
2156
- const type = response.type;
2157
- const contentTypes = (_c = (_b = operation.operation.responses[0]) === null || _b === void 0 ? void 0 : _b.contentTypes) !== null && _c !== void 0 ? _c : [];
2158
- // Determine whether to wrap based on the HLC PropertyKind mapping.
2159
- // HLC wraps with `body` only when the type is PropertyKind.Primitive or PropertyKind.Enum
2160
- // (i.e. NOT PropertyKind.Composite or PropertyKind.Dictionary).
2161
- // For array types, HLC uses the item's kind, not the array itself.
2162
- //
2163
- // Case: bytes with binary content type → binary wrap (isBinary=true)
2267
+ const contentTypes = (_e = (_d = operation.operation.responses[0]) === null || _d === void 0 ? void 0 : _d.contentTypes) !== null && _e !== void 0 ? _e : [];
2268
+ // bytes with binary content type binary wrap (isBinary=true)
2164
2269
  // HLC: bytes → binary payload → separate binary handling
2165
2270
  if (type.__raw && isBinaryPayload(context, type.__raw, contentTypes)) {
2166
2271
  return { shouldWrap: true, isBinary: true };
2167
2272
  }
2168
- // Case: model array (e.g. Foo[]) no wrap
2169
- // HLC: model array → PropertyKind.Composite (item kind = model) → no body wrapper
2170
- // Modular: array with valueType.kind === "model"
2171
- if (type.kind === "array" && type.valueType.kind === "model") {
2172
- return noWrap;
2173
- }
2174
- // Case: any object / Record (e.g. Record<string, unknown>) → no wrap
2175
- // HLC: SchemaType.AnyObject → PropertyKind.Dictionary → no body wrapper
2176
- // Modular: kind === "dict"
2177
- // Case: model → no wrap
2178
- // HLC: model → PropertyKind.Composite → no body wrapper
2179
- // Modular: kind === "model"
2180
- if (type.kind === "dict" || type.kind === "model") {
2181
- return noWrap;
2182
- }
2183
- // Case: unknown with treatUnknownAsRecord enabled → no wrap
2184
- // When treatUnknownAsRecord is enabled, `unknown` is treated as Record<string, unknown>
2185
- // which maps to HLC PropertyKind.Dictionary → no body wrapper
2186
- if (type.kind === "unknown" && ((_d = context.rlcOptions) === null || _d === void 0 ? void 0 : _d.treatUnknownAsRecord)) {
2187
- return noWrap;
2188
- }
2189
- // Remaining cases → wrap with `body`:
2190
- // - string → HLC PropertyKind.Primitive → modular `string`
2191
- // - boolean → HLC PropertyKind.Primitive → modular `boolean`
2192
- // - string[] → HLC PropertyKind.Primitive (array keeps item kind) → modular `string[]`
2193
- // - any → HLC PropertyKind.Primitive → modular `unknown` or `any`
2194
- // - enum → HLC PropertyKind.Enum → modular `KnownXxx | string`
2195
- return { shouldWrap: true, isBinary: false };
2273
+ return { shouldWrap: isWrappableType(context, type), isBinary: false };
2196
2274
  }
2197
2275
  /**
2198
2276
  * Builds a TypeAliasDeclarationStructure for the non-model response wrapper type.