czh-api 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,10 +1,35 @@
1
1
  # 更新日志
2
2
 
3
- 所有重要的版本更新都会记录在此文件中。
4
-
5
- ## [1.0.3] - 2026-03-17
6
-
7
- ### 新增
3
+ 所有重要的版本更新都会记录在此文件中。
4
+
5
+ ## [1.0.5] - 2026-03-19
6
+
7
+ ### 修复
8
+ - 修复 FastAPI 场景下返回类型字段被错误降级为 `any` 的问题(如 `items.$ref`、`anyOf`、`oneOf`、`allOf` 组合场景)。
9
+ - 修复数组响应与嵌套对象类型在 fallback 生成流程中丢失字段的问题,避免出现仅有 `{ [key: string]: any }` 的空壳接口。
10
+ - 修复 `group_ids` 等多层结构类型解析不完整的问题,支持递归解析到更深层级(如 `number[] | null`)。
11
+ - 修复类型增强阶段对复杂类型名(数组、联合类型)处理不稳定导致的类型声明异常问题。
12
+
13
+ ### 改进
14
+ - 增强 fallback 类型生成策略:优先基于 `module.schemas` 还原字段结构,再回退到 `any`,生成结果更可用。
15
+ - 增强类型依赖收集能力:从字段类型表达式中提取嵌套引用类型并自动补齐声明。
16
+ - 完善 README 模板示例并与当前默认模板保持一致(`api.hbs`、`index.hbs`、`types.hbs`)。
17
+
18
+ ## [1.0.4] - 2026-03-19
19
+
20
+ ### 修复
21
+ - 修复响应最外层为数组(如 `type: array` + `items.$ref`)时类型生成异常的问题,避免出现非法接口声明(如 `export interface Xxx[]`)。
22
+ - 修复仅在“数组响应”中被引用的实体类型可能退化为 `{ [key: string]: any }` 的问题,增强该场景下的类型补全稳定性。
23
+ - 修复类型增强阶段对 `export interface` 的替换逻辑在存在内联对象字段时可能截断内容的问题,避免生成残留字段到接口外。
24
+ - 修复部分 OpenAPI `$ref` 路径在类型编译阶段解析不一致导致的类型降级问题(兼容 `#/components/schemas/*` 场景)。
25
+
26
+ ### 改进
27
+ - 优化数组响应类型推导策略:当响应为 `$ref` 数组时优先生成 `ItemType[]`,减少中间响应类型噪音。
28
+ - 增强类型生成兜底逻辑的类型名合法性校验,避免对泛型/数组等复杂类型名误生成 `interface`。
29
+
30
+ ## [1.0.3] - 2026-03-17
31
+
32
+ ### 新增
8
33
  - 添加 `pathPrefixes` 配置项,支持自定义路径前缀分组和二级分包
9
34
  - 支持配置多个路径前缀,每个前缀可指定自定义包名或自动驼峰命名
10
35
  - 自动按路径前缀后的第一级路径进行二级分包
package/README.md CHANGED
@@ -190,12 +190,7 @@ export const {{functionName}} = ({{#if hasParams}}params: {{requestParamsTypeNam
190
190
  return http.request<{{responseTypeName}}>({
191
191
  url: `{{path}}`,
192
192
  method: '{{method}}',
193
- {{#if hasParams}}
194
- params,
195
- {{/if}}
196
- {{#if hasData}}
197
- data,
198
- {{/if}}
193
+ {{#if hasParams}}{{#unless (eq method 'put')}}{{#unless (eq method 'post')}}params,{{/unless}}{{/unless}}{{/if}}{{#if hasData}}data,{{/if}}
199
194
  {{#if contentType}}
200
195
  headers: { 'Content-Type': '{{contentType}}' },
201
196
  {{/if}}
@@ -209,11 +204,9 @@ export const {{functionName}} = ({{#if hasParams}}params: {{requestParamsTypeNam
209
204
  export const {{functionName}} = ({{#if hasParams}}params: {{requestParamsTypeName}}{{/if}}{{#if hasData}}{{#if hasParams}}, {{/if}}data: {{requestBodyTypeName}}{{/if}}) => {
210
205
  return request.{{#if (eq method "delete")}}del{{else}}{{method}}{{/if}}<{{responseTypeName}}>({
211
206
  url: `{{path}}`,
212
- {{#if hasParams}}
213
- params,
214
- {{/if}}
215
- {{#if hasData}}
216
- data,
207
+ {{#if hasParams}}{{#unless (eq method 'put')}}{{#unless (eq method 'post')}}params,{{/unless}}{{/unless}}{{/if}}{{#if hasData}}data,{{/if}}
208
+ {{#if contentType}}
209
+ headers: { 'Content-Type': '{{contentType}}' },
217
210
  {{/if}}
218
211
  });
219
212
  };
@@ -48,68 +48,236 @@ function convertOpenApiTypeToTypeScript(openApiType) {
48
48
  case 'number':
49
49
  return 'number';
50
50
  default:
51
- return 'any';
51
+ return openApiType;
52
52
  }
53
53
  }
54
54
  // 改进类型定义的辅助函数
55
+ function isValidTypeIdentifier(typeName) {
56
+ if (!typeName)
57
+ return false;
58
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(typeName);
59
+ }
60
+ function escapeRegex(value) {
61
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
62
+ }
63
+ function resolveInterfaceTargetType(typeName) {
64
+ if (typeof typeName !== 'string' || typeName.length === 0)
65
+ return null;
66
+ if (isValidTypeIdentifier(typeName))
67
+ return typeName;
68
+ const arrayMatch = typeName.match(/^([A-Za-z_$][A-Za-z0-9_$]*)\[\]$/);
69
+ if (arrayMatch) {
70
+ return arrayMatch[1];
71
+ }
72
+ return null;
73
+ }
74
+ function findInterfaceBlockRange(content, typeName) {
75
+ const startRegex = new RegExp(`export interface ${escapeRegex(typeName)}\\s*\\{`);
76
+ const match = startRegex.exec(content);
77
+ if (!match)
78
+ return null;
79
+ const start = match.index;
80
+ const openBraceIndex = start + match[0].lastIndexOf('{');
81
+ let depth = 0;
82
+ for (let i = openBraceIndex; i < content.length; i++) {
83
+ const ch = content[i];
84
+ if (ch === '{') {
85
+ depth++;
86
+ }
87
+ else if (ch === '}') {
88
+ depth--;
89
+ if (depth === 0) {
90
+ return { start, end: i + 1 };
91
+ }
92
+ }
93
+ }
94
+ return null;
95
+ }
96
+ const TS_BUILTIN_TYPE_NAMES = new Set([
97
+ 'string',
98
+ 'number',
99
+ 'boolean',
100
+ 'null',
101
+ 'undefined',
102
+ 'void',
103
+ 'any',
104
+ 'unknown',
105
+ 'never',
106
+ 'object',
107
+ 'Record',
108
+ 'Array',
109
+ 'Date',
110
+ 'Promise',
111
+ 'true',
112
+ 'false',
113
+ ]);
114
+ function extractTypeIdentifiers(typeExpr) {
115
+ if (!typeExpr)
116
+ return [];
117
+ const matches = typeExpr.match(/[A-Za-z_$][A-Za-z0-9_$]*/g) || [];
118
+ return [...new Set(matches.filter(name => !TS_BUILTIN_TYPE_NAMES.has(name)))];
119
+ }
120
+ function getSchemaNameFromRef(ref) {
121
+ if (!ref)
122
+ return '';
123
+ return ref.split('/').pop() || '';
124
+ }
125
+ function resolveSchemaTypeFromModuleSchemas(schema, moduleSchemas, visitedRefs = new Set()) {
126
+ if (!schema)
127
+ return 'any';
128
+ if (schema.$ref) {
129
+ const refName = getSchemaNameFromRef(schema.$ref);
130
+ return refName || 'any';
131
+ }
132
+ const schemaObj = schema;
133
+ if (schemaObj.anyOf && Array.isArray(schemaObj.anyOf) && schemaObj.anyOf.length > 0) {
134
+ const types = schemaObj.anyOf
135
+ .map(item => resolveSchemaTypeFromModuleSchemas(item, moduleSchemas, visitedRefs))
136
+ .filter(Boolean);
137
+ if (types.length > 0) {
138
+ return [...new Set(types)].join(' | ');
139
+ }
140
+ }
141
+ if (schemaObj.oneOf && Array.isArray(schemaObj.oneOf) && schemaObj.oneOf.length > 0) {
142
+ const types = schemaObj.oneOf
143
+ .map(item => resolveSchemaTypeFromModuleSchemas(item, moduleSchemas, visitedRefs))
144
+ .filter(Boolean);
145
+ if (types.length > 0) {
146
+ return [...new Set(types)].join(' | ');
147
+ }
148
+ }
149
+ if (schemaObj.type === 'array') {
150
+ const itemType = resolveSchemaTypeFromModuleSchemas(schemaObj.items, moduleSchemas, visitedRefs);
151
+ return `${itemType || 'any'}[]`;
152
+ }
153
+ if (schemaObj.type === 'object' && schemaObj.additionalProperties) {
154
+ if (schemaObj.additionalProperties === true) {
155
+ return 'Record<string, any>';
156
+ }
157
+ const valueType = resolveSchemaTypeFromModuleSchemas(schemaObj.additionalProperties, moduleSchemas, visitedRefs);
158
+ return `Record<string, ${valueType || 'any'}>`;
159
+ }
160
+ if (schemaObj.type === 'object' && schemaObj.properties && Object.keys(schemaObj.properties).length > 0) {
161
+ const requiredSet = new Set(schemaObj.required || []);
162
+ const inlineFields = Object.entries(schemaObj.properties).map(([key, value]) => {
163
+ const fieldType = resolveSchemaTypeFromModuleSchemas(value, moduleSchemas, visitedRefs);
164
+ const optional = requiredSet.has(key) ? '' : '?';
165
+ return `${key}${optional}: ${fieldType || 'any'}`;
166
+ });
167
+ return `{ ${inlineFields.join('; ')} }`;
168
+ }
169
+ const baseType = convertOpenApiTypeToTypeScript(schemaObj.type);
170
+ if (!baseType)
171
+ return 'any';
172
+ return schemaObj.nullable ? `${baseType} | null` : baseType;
173
+ }
174
+ function collectSchemaProperties(schema, moduleSchemas, visitedRefs = new Set()) {
175
+ const collected = {
176
+ properties: {},
177
+ required: new Set(),
178
+ };
179
+ if (!schema)
180
+ return collected;
181
+ if (schema.$ref) {
182
+ const ref = schema.$ref;
183
+ if (!visitedRefs.has(ref)) {
184
+ visitedRefs.add(ref);
185
+ const refName = getSchemaNameFromRef(ref);
186
+ const refSchema = moduleSchemas[refName];
187
+ if (refSchema) {
188
+ const nested = collectSchemaProperties(refSchema, moduleSchemas, visitedRefs);
189
+ Object.assign(collected.properties, nested.properties);
190
+ nested.required.forEach(field => collected.required.add(field));
191
+ }
192
+ }
193
+ return collected;
194
+ }
195
+ const schemaObj = schema;
196
+ if (schemaObj.allOf && Array.isArray(schemaObj.allOf)) {
197
+ schemaObj.allOf.forEach(item => {
198
+ const nested = collectSchemaProperties(item, moduleSchemas, visitedRefs);
199
+ Object.assign(collected.properties, nested.properties);
200
+ nested.required.forEach(field => collected.required.add(field));
201
+ });
202
+ }
203
+ if (schemaObj.properties) {
204
+ Object.entries(schemaObj.properties).forEach(([name, value]) => {
205
+ collected.properties[name] = value;
206
+ });
207
+ }
208
+ (schemaObj.required || []).forEach(field => collected.required.add(field));
209
+ return collected;
210
+ }
211
+ function buildInterfaceDefinitionFromModuleSchemas(typeName, moduleSchemas) {
212
+ const schema = moduleSchemas[typeName];
213
+ if (!schema)
214
+ return null;
215
+ const { properties, required } = collectSchemaProperties(schema, moduleSchemas);
216
+ const entries = Object.entries(properties);
217
+ if (entries.length === 0)
218
+ return null;
219
+ const lines = entries.map(([propName, propSchema]) => {
220
+ const optional = required.has(propName) ? '' : '?';
221
+ const propType = resolveSchemaTypeFromModuleSchemas(propSchema, moduleSchemas) || 'any';
222
+ const propDescription = propSchema.description || '';
223
+ const comment = propDescription ? ` // ${propDescription}` : '';
224
+ return ` ${propName}${optional}: ${propType};${comment}`;
225
+ });
226
+ return `export interface ${typeName} {\n${lines.join('\n')}\n}`;
227
+ }
55
228
  function improveTypeDefinitions(typesContent, endpoints) {
56
229
  let improvedContent = typesContent;
57
230
  // 收集所有需要改进的类型定义
58
231
  const typeImprovements = {};
59
- endpoints.forEach(endpoint => {
60
- // 改进 params 类型
61
- if (endpoint.requestParamsTypeName && endpoint.jsdocParams) {
62
- const paramsFields = endpoint.jsdocParams
63
- .filter((param) => param.name && param.type)
64
- .map((param) => {
65
- const optional = !param.required ? '?' : '';
66
- let type = param.type;
67
- // 处理联合类型,确保正确的 TypeScript 语法
68
- if (type && type.includes(' | ')) {
69
- // 如果包含 null,确保格式正确
70
- if (type.includes('null')) {
71
- type = type.replace(/\s*\|\s*null/g, ' | null');
72
- }
73
- }
74
- else {
75
- // 处理基本类型转换
76
- type = convertOpenApiTypeToTypeScript(type);
232
+ // 辅助函数:将 jsdoc params 转换为接口字段
233
+ function buildFieldsFromJsdocParams(jsdocParams) {
234
+ return jsdocParams
235
+ .filter((param) => param.name && param.type)
236
+ .map((param) => {
237
+ const optional = !param.required ? '?' : '';
238
+ let type = param.type;
239
+ // 处理联合类型,确保正确的 TypeScript 语法
240
+ if (type && type.includes(' | ')) {
241
+ if (type.includes('null')) {
242
+ type = type.replace(/\s*\|\s*null/g, ' | null');
77
243
  }
78
- const comment = param.description ? ` // ${param.description}` : '';
79
- return ` ${param.name}${optional}: ${type};${comment}`;
80
- })
81
- .join('\n');
244
+ }
245
+ else {
246
+ type = convertOpenApiTypeToTypeScript(type);
247
+ }
248
+ const comment = param.description ? ` // ${param.description}` : '';
249
+ return ` ${param.name}${optional}: ${type};${comment}`;
250
+ })
251
+ .join('\n');
252
+ }
253
+ endpoints.forEach(endpoint => {
254
+ // 改进 params 类型 - 使用 paramsJsdocParams(仅包含 path/query 参数)
255
+ const requestParamsTypeName = endpoint.requestParamsTypeName;
256
+ if (requestParamsTypeName && isValidTypeIdentifier(requestParamsTypeName) && endpoint.paramsJsdocParams && endpoint.paramsJsdocParams.length > 0) {
257
+ const paramsFields = buildFieldsFromJsdocParams(endpoint.paramsJsdocParams);
82
258
  if (paramsFields) {
83
- typeImprovements[endpoint.requestParamsTypeName] = `export interface ${endpoint.requestParamsTypeName} {
84
- ${paramsFields}
259
+ typeImprovements[requestParamsTypeName] = `export interface ${requestParamsTypeName} {
260
+ ${paramsFields}
85
261
  }`;
86
262
  }
87
263
  }
88
- // 改进 requestBody 类型
89
- if (endpoint.requestBodyTypeName && endpoint.jsdocParams) {
90
- const dataFields = endpoint.jsdocParams
91
- .filter((param) => param.name && param.type)
92
- .map((param) => {
93
- const optional = !param.required ? '?' : '';
94
- let type = param.type;
95
- // 处理联合类型,确保正确的 TypeScript 语法
96
- if (type && type.includes(' | ')) {
97
- // 如果包含 null,确保格式正确
98
- if (type.includes('null')) {
99
- type = type.replace(/\s*\|\s*null/g, ' | null');
100
- }
101
- }
102
- else {
103
- // 处理基本类型转换
104
- type = convertOpenApiTypeToTypeScript(type);
105
- }
106
- const comment = param.description ? ` // ${param.description}` : '';
107
- return ` ${param.name}${optional}: ${type};${comment}`;
108
- })
109
- .join('\n');
264
+ // 改进 requestBody 类型 - 使用 dataJsdocParams(仅包含 requestBody 参数)
265
+ const requestBodyTypeName = endpoint.requestBodyTypeName;
266
+ if (requestBodyTypeName && isValidTypeIdentifier(requestBodyTypeName) && endpoint.dataJsdocParams && endpoint.dataJsdocParams.length > 0) {
267
+ const dataFields = buildFieldsFromJsdocParams(endpoint.dataJsdocParams);
110
268
  if (dataFields) {
111
- typeImprovements[endpoint.requestBodyTypeName] = `export interface ${endpoint.requestBodyTypeName} {
112
- ${dataFields}
269
+ typeImprovements[requestBodyTypeName] = `export interface ${requestBodyTypeName} {
270
+ ${dataFields}
271
+ }`;
272
+ }
273
+ }
274
+ // 改进 response 类型 - 使用 responseJsdocParams(包含响应字段信息)
275
+ const responseTargetType = resolveInterfaceTargetType(endpoint.responseTypeName);
276
+ if (responseTargetType && endpoint.responseTypeName !== 'void' && endpoint.responseJsdocParams && endpoint.responseJsdocParams.length > 0) {
277
+ const responseFields = buildFieldsFromJsdocParams(endpoint.responseJsdocParams);
278
+ if (responseFields) {
279
+ typeImprovements[responseTargetType] = `export interface ${responseTargetType} {
280
+ ${responseFields}
113
281
  }`;
114
282
  }
115
283
  }
@@ -117,9 +285,12 @@ ${dataFields}
117
285
  // 替换现有的类型定义
118
286
  Object.entries(typeImprovements).forEach(([typeName, newDefinition]) => {
119
287
  // 查找并替换现有的接口定义
120
- const interfaceRegex = new RegExp(`export interface ${typeName}\\s*\\{[^}]*\\}`, 'g');
121
- if (interfaceRegex.test(improvedContent)) {
122
- improvedContent = improvedContent.replace(interfaceRegex, newDefinition);
288
+ const blockRange = findInterfaceBlockRange(improvedContent, typeName);
289
+ if (blockRange) {
290
+ improvedContent =
291
+ improvedContent.slice(0, blockRange.start) +
292
+ newDefinition +
293
+ improvedContent.slice(blockRange.end);
123
294
  }
124
295
  else {
125
296
  // 如果没找到接口定义,添加到末尾
@@ -186,6 +357,33 @@ function removeUselessTypeAliases(typesContent) {
186
357
  cleanedContent = cleanedContent.replace(/\/\*\*\n \* [^\n]*\n \*\/\n(?!export)/g, '');
187
358
  return cleanedContent;
188
359
  }
360
+ function normalizeRef(refValue) {
361
+ const match = refValue.match(/^#\/components\/schemas\/([^/]+)$/);
362
+ if (match) {
363
+ return `#/definitions/${match[1]}`;
364
+ }
365
+ return refValue;
366
+ }
367
+ function normalizeSchemaRefs(value) {
368
+ if (Array.isArray(value)) {
369
+ return value.map(item => normalizeSchemaRefs(item));
370
+ }
371
+ if (value && typeof value === 'object') {
372
+ const obj = value;
373
+ const normalized = {};
374
+ for (const key in obj) {
375
+ const currentValue = obj[key];
376
+ if (key === '$ref' && typeof currentValue === 'string') {
377
+ normalized[key] = normalizeRef(currentValue);
378
+ }
379
+ else {
380
+ normalized[key] = normalizeSchemaRefs(currentValue);
381
+ }
382
+ }
383
+ return normalized;
384
+ }
385
+ return value;
386
+ }
189
387
  const handleBuild = () => __awaiter(void 0, void 0, void 0, function* () {
190
388
  try {
191
389
  console.log(chalk_1.default.blue('Building API from source...'));
@@ -285,9 +483,10 @@ const handleBuild = () => __awaiter(void 0, void 0, void 0, function* () {
285
483
  if (Object.keys(module.schemas).length > 0) {
286
484
  console.log(chalk_1.default.blue(`正在为模块 "${moduleName}" 生成类型文件,包含 ${Object.keys(module.schemas).length} 个 schema`));
287
485
  try {
486
+ const normalizedModuleSchemas = normalizeSchemaRefs(module.schemas);
288
487
  const schemasWithTitles = {};
289
- for (const schemaName in module.schemas) {
290
- schemasWithTitles[schemaName] = Object.assign(Object.assign({}, module.schemas[schemaName]), { title: schemaName });
488
+ for (const schemaName in normalizedModuleSchemas) {
489
+ schemasWithTitles[schemaName] = Object.assign(Object.assign({}, normalizedModuleSchemas[schemaName]), { title: schemaName });
291
490
  }
292
491
  const rootSchemaForCompiler = {
293
492
  title: 'schemas',
@@ -334,7 +533,8 @@ const handleBuild = () => __awaiter(void 0, void 0, void 0, function* () {
334
533
  // 尝试逐个验证 schema,过滤掉有问题的
335
534
  const validSchemas = {};
336
535
  const invalidSchemas = [];
337
- for (const schemaName in module.schemas) {
536
+ const normalizedModuleSchemas = normalizeSchemaRefs(module.schemas);
537
+ for (const schemaName in normalizedModuleSchemas) {
338
538
  try {
339
539
  // 尝试单独编译每个 schema
340
540
  const testSchema = {
@@ -342,11 +542,11 @@ const handleBuild = () => __awaiter(void 0, void 0, void 0, function* () {
342
542
  type: 'object',
343
543
  properties: {},
344
544
  additionalProperties: false,
345
- definitions: { [schemaName]: module.schemas[schemaName] },
346
- components: { schemas: { [schemaName]: module.schemas[schemaName] } }
545
+ definitions: { [schemaName]: normalizedModuleSchemas[schemaName] },
546
+ components: { schemas: { [schemaName]: normalizedModuleSchemas[schemaName] } }
347
547
  };
348
548
  yield (0, json_schema_to_typescript_1.compile)(testSchema, 'test', { unreachableDefinitions: true });
349
- validSchemas[schemaName] = Object.assign(Object.assign({}, module.schemas[schemaName]), { title: schemaName });
549
+ validSchemas[schemaName] = Object.assign(Object.assign({}, normalizedModuleSchemas[schemaName]), { title: schemaName });
350
550
  }
351
551
  catch (schemaError) {
352
552
  invalidSchemas.push(schemaName);
@@ -413,70 +613,83 @@ const handleBuild = () => __awaiter(void 0, void 0, void 0, function* () {
413
613
  console.log(chalk_1.default.blue(`为模块 "${moduleName}" 生成基于 JSDoc 的类型定义...`));
414
614
  // 创建类型定义映射
415
615
  const typeDefinitions = {};
616
+ const nestedReferencedTypes = new Set();
416
617
  // 从 endpoints 中提取类型定义
417
618
  module.endpoints.forEach(endpoint => {
418
- // 处理 params 类型
419
- if (endpoint.requestParamsTypeName && endpoint.jsdocParams) {
420
- const paramsFields = endpoint.jsdocParams
421
- .filter(param => param.name && param.type)
422
- .map(param => {
423
- const optional = !param.required ? '?' : '';
424
- let type = param.type;
425
- // 处理联合类型
426
- if (type && type.includes(' | ')) {
427
- // 如果包含 null,确保格式正确
428
- if (type.includes('null')) {
429
- type = type.replace(/\s*\|\s*null/g, ' | null');
430
- }
619
+ // 辅助函数:将 jsdoc params 转换为字段
620
+ const buildFields = (params) => params
621
+ .filter((param) => param.name && param.type)
622
+ .map((param) => {
623
+ const optional = !param.required ? '?' : '';
624
+ let type = param.type;
625
+ if (type && type.includes(' | ')) {
626
+ if (type.includes('null')) {
627
+ type = type.replace(/\s*\|\s*null/g, ' | null');
431
628
  }
432
- else {
433
- // 处理基本类型转换
434
- type = convertOpenApiTypeToTypeScript(type);
629
+ }
630
+ else {
631
+ type = convertOpenApiTypeToTypeScript(type);
632
+ }
633
+ extractTypeIdentifiers(type).forEach(typeName => {
634
+ if (isValidTypeIdentifier(typeName)) {
635
+ nestedReferencedTypes.add(typeName);
435
636
  }
436
- const comment = param.description ? ` // ${param.description}` : '';
437
- return ` ${param.name}${optional}: ${type};${comment}`;
438
- })
439
- .join('\n');
637
+ });
638
+ const comment = param.description ? ` // ${param.description}` : '';
639
+ return ` ${param.name}${optional}: ${type};${comment}`;
640
+ })
641
+ .join('\n');
642
+ // 处理 params 类型 - 使用 paramsJsdocParams
643
+ const requestParamsTypeName = endpoint.requestParamsTypeName;
644
+ if (requestParamsTypeName && isValidTypeIdentifier(requestParamsTypeName) && endpoint.paramsJsdocParams && endpoint.paramsJsdocParams.length > 0) {
645
+ const paramsFields = buildFields(endpoint.paramsJsdocParams);
440
646
  if (paramsFields) {
441
- typeDefinitions[endpoint.requestParamsTypeName] = `export interface ${endpoint.requestParamsTypeName} {
442
- ${paramsFields}
647
+ typeDefinitions[requestParamsTypeName] = `export interface ${requestParamsTypeName} {
648
+ ${paramsFields}
443
649
  }`;
444
650
  }
445
651
  }
446
- // 处理 data 类型 (requestBody)
447
- if (endpoint.requestBodyTypeName && endpoint.jsdocParams) {
448
- const dataFields = endpoint.jsdocParams
449
- .filter(param => param.name && param.type)
450
- .map(param => {
451
- const optional = !param.required ? '?' : '';
452
- let type = param.type;
453
- // 处理联合类型
454
- if (type && type.includes(' | ')) {
455
- // 如果包含 null,确保格式正确
456
- if (type.includes('null')) {
457
- type = type.replace(/\s*\|\s*null/g, ' | null');
458
- }
459
- }
460
- else {
461
- // 处理基本类型转换
462
- type = convertOpenApiTypeToTypeScript(type);
463
- }
464
- const comment = param.description ? ` // ${param.description}` : '';
465
- return ` ${param.name}${optional}: ${type};${comment}`;
466
- })
467
- .join('\n');
652
+ // 处理 data 类型 (requestBody) - 使用 dataJsdocParams
653
+ const requestBodyTypeName = endpoint.requestBodyTypeName;
654
+ if (requestBodyTypeName && isValidTypeIdentifier(requestBodyTypeName) && endpoint.dataJsdocParams && endpoint.dataJsdocParams.length > 0) {
655
+ const dataFields = buildFields(endpoint.dataJsdocParams);
468
656
  if (dataFields) {
469
- typeDefinitions[endpoint.requestBodyTypeName] = `export interface ${endpoint.requestBodyTypeName} {
470
- ${dataFields}
657
+ typeDefinitions[requestBodyTypeName] = `export interface ${requestBodyTypeName} {
658
+ ${dataFields}
659
+ }`;
660
+ }
661
+ }
662
+ // 处理 response 类型 - 使用 responseJsdocParams
663
+ const responseTargetType = resolveInterfaceTargetType(endpoint.responseTypeName);
664
+ if (responseTargetType && endpoint.responseTypeName !== 'void' && endpoint.responseJsdocParams && endpoint.responseJsdocParams.length > 0) {
665
+ const responseFields = buildFields(endpoint.responseJsdocParams);
666
+ if (responseFields) {
667
+ typeDefinitions[responseTargetType] = `export interface ${responseTargetType} {
668
+ ${responseFields}
471
669
  }`;
472
670
  }
473
671
  }
474
672
  });
475
673
  // 为没有定义的类型添加基本定义
476
- allReferencedTypes.forEach(typeName => {
674
+ const fallbackReferencedTypes = [
675
+ ...new Set([
676
+ ...allReferencedTypes,
677
+ ...Object.keys(module.schemas || {}),
678
+ ...nestedReferencedTypes,
679
+ ])
680
+ ];
681
+ fallbackReferencedTypes.forEach(typeName => {
682
+ if (!isValidTypeIdentifier(typeName)) {
683
+ return;
684
+ }
477
685
  if (!typeDefinitions[typeName]) {
478
- typeDefinitions[typeName] = `export interface ${typeName} {
479
- [key: string]: any;
686
+ const schemaBasedDefinition = buildInterfaceDefinitionFromModuleSchemas(typeName, module.schemas);
687
+ if (schemaBasedDefinition) {
688
+ typeDefinitions[typeName] = schemaBasedDefinition;
689
+ return;
690
+ }
691
+ typeDefinitions[typeName] = `export interface ${typeName} {
692
+ [key: string]: any;
480
693
  }`;
481
694
  }
482
695
  });