Skip to content

Commit 9997588

Browse files
committed
fix: xxxOf可能与任意结构组合
1 parent ba3dbf8 commit 9997588

File tree

7 files changed

+344
-121
lines changed

7 files changed

+344
-121
lines changed

src/lib/generate-template.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,10 @@ declare namespace ${className} {
4848
let content = metas[method].flatMap((meta) => {
4949
let opts: string[] = [];
5050
51-
(<const>['query', 'params']).forEach((key) => {
51+
(<const>['query', 'params', 'body', 'response']).forEach((key) => {
5252
const interfaceName = upperFirst(camelCase(meta.key + '_' + key));
5353
if (meta[key].types.length) {
54-
opts.push(`interface ${interfaceName} ${meta[key].types[0]}\n`);
55-
}
56-
});
57-
58-
(<const>['body', 'response']).forEach((key) => {
59-
const interfaceName = upperFirst(camelCase(meta.key + '_' + key));
60-
if (meta[key].types.length) {
61-
opts.push(
62-
meta[key].types.length === 1
63-
? `interface ${interfaceName} ${meta[key].types}\n`
64-
: `type ${interfaceName} = ${meta[key].types.join(' | ')}\n`,
65-
);
54+
opts.push(`type ${interfaceName} = ${meta[key].types.join(' | ')}\n`);
6655
}
6756
});
6857

src/lib/parse-schema.ts

Lines changed: 86 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,105 @@ export const parseSchema = (
77
schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject,
88
): string => {
99
const parsed = refToObject(docs, schema);
10-
const nullable = parsed.nullable ? ' | null' : '';
10+
const { ofType, nullable } = parseOf(docs, parsed);
1111

12-
if (parsed.enum?.length) {
13-
return `${parsed.enum.map((item) => (typeof item === 'number' ? item : `"${item}"`)).join(' | ')}${nullable}`;
14-
}
12+
if (ofType) return `(${ofType}${nullable})`;
1513

16-
if (parsed.oneOf) {
17-
return parsed.oneOf.map((schema) => parseSchema(docs, schema)).join(' | ');
14+
if (parsed.enum?.length) {
15+
return `(${parsed.enum.map((item) => (typeof item === 'number' ? item : `"${item}"`)).join(' | ')}${nullable})`;
1816
}
1917

2018
switch (parsed.type) {
21-
case 'array':
22-
return `(${parseSchema(docs, parsed.items)})[]${nullable}`;
2319
case 'boolean':
24-
return `boolean${nullable}`;
20+
return `(boolean${nullable})`;
2521
case 'integer':
2622
case 'number':
27-
return `number${nullable}`;
28-
case 'object':
23+
return `(number${nullable})`;
24+
case 'string':
25+
if (parsed.format === 'binary') return `(Blob${nullable})`;
26+
return `(string${nullable})`;
27+
case 'array':
28+
return `((${parseSchema(docs, parsed.items)})[]${nullable})`;
29+
case 'object': {
2930
const requiredProperties = parsed.required || [];
3031
const properties = Object.entries(parsed.properties || {}).map(([key, schema]) => {
3132
const schemaObj = refToObject(docs, schema);
3233
return `${generateComments(schemaObj)}${key}${requiredProperties.includes(key) ? '' : '?'}: ${parseSchema(docs, schemaObj)}`;
3334
});
34-
return `{ ${properties.join(';')} }${nullable}`;
35-
case 'string':
36-
if (parsed.format === 'binary') return `Blob${nullable}`;
37-
return `string${nullable}`;
35+
return `({ ${properties.join(';')} }${nullable})`;
36+
}
37+
default:
38+
return 'unknown';
39+
}
40+
};
41+
42+
const parseOf = (
43+
docs: OpenAPIV3.Document,
44+
schema: OpenAPIV3.SchemaObject,
45+
): { nullable: string; ofType: string } => {
46+
if (!schema.oneOf?.length && !schema.anyOf?.length && !schema.allOf?.length) {
47+
return {
48+
nullable: schema.nullable ? ' | null' : '',
49+
ofType: '',
50+
};
51+
}
52+
53+
const { oneOf = [], anyOf = [], allOf = [], nullable = false, ...rest } = schema;
54+
const oneTypes = new Set<string>();
55+
const anyTypes = new Set<string>();
56+
const allTypes = new Set<string>();
57+
58+
const nullableSet = new Set<boolean>(
59+
oneOf
60+
.concat(anyOf)
61+
.concat(allOf)
62+
.map((item) => {
63+
const { nullable: itemNullable } = refToObject(docs, item);
64+
if (itemNullable !== undefined) return itemNullable;
65+
return nullable;
66+
}),
67+
);
68+
const additionalMergeOpts = nullableSet.size > 1 ? { nullable } : {};
69+
70+
for (const one of oneOf) {
71+
oneTypes.add(
72+
parseSchema(docs, { ...rest, ...additionalMergeOpts, ...refToObject(docs, one) }),
73+
);
74+
}
75+
76+
for (const any of anyOf) {
77+
anyTypes.add(
78+
parseSchema(docs, { ...rest, ...additionalMergeOpts, ...refToObject(docs, any) }),
79+
);
80+
}
81+
82+
for (const all of allOf) {
83+
allTypes.add(
84+
parseSchema(docs, { ...rest, ...additionalMergeOpts, ...refToObject(docs, all) }),
85+
);
86+
}
87+
88+
let oneType = oneTypes.size ? `(${[...oneTypes].join(' | ')})` : '';
89+
let anyType = anyTypes.size ? `(${[...anyTypes].join(' | ')})` : '';
90+
let allType = allTypes.size ? `(${[...allTypes].join(' & ')})` : '';
91+
92+
if (oneType && oneType === anyType) anyType = '';
93+
if (oneType && oneType === allType) {
94+
allType = '';
95+
anyType = '';
96+
} else if (anyType && anyType === allType) {
97+
allType = '';
98+
oneType = '';
3899
}
39100

40-
return 'unknown';
101+
let unionType = [oneType, anyType].filter(Boolean).join(' | ');
102+
if (unionType) unionType = `(${unionType})`;
103+
104+
let finalType = [unionType, allType].filter(Boolean).join(' & ');
105+
if (finalType) finalType = `(${finalType})`;
106+
107+
return {
108+
nullable: nullableSet.size === 1 && [...nullableSet][0] === true ? ' | null' : '',
109+
ofType: finalType,
110+
};
41111
};

test/lib/generate-template.test.ts

Lines changed: 16 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,10 @@ test('完整的类型提示', async () => {
6464
{
6565
"OpenapiClient": {
6666
"dts": "declare namespace OpenapiClient {
67-
interface GetUsersQuery {
68-
foo?: string;
69-
bar?: string;
70-
}
71-
interface GetUsersParams {
72-
baz: number;
73-
}
74-
interface GetUsersBody {}
75-
interface GetUsersResponse {
76-
foo?: string;
77-
}
67+
type GetUsersQuery = { foo?: string; bar?: string };
68+
type GetUsersParams = { baz: number };
69+
type GetUsersBody = {};
70+
type GetUsersResponse = { foo?: string };
7871
}
7972
8073
declare class OpenapiClient<T extends object = object> extends BaseOpenapiClient<T> {
@@ -124,17 +117,10 @@ test('完整的类型提示', async () => {
124117
{
125118
"OpenapiClient": {
126119
"dts": "declare namespace OpenapiClient {
127-
interface GetUsersQuery {
128-
foo?: string;
129-
bar?: string;
130-
}
131-
interface GetUsersParams {
132-
baz: number;
133-
}
134-
interface GetUsersBody {}
135-
interface GetUsersResponse {
136-
foo?: string;
137-
}
120+
type GetUsersQuery = { foo?: string; bar?: string };
121+
type GetUsersParams = { baz: number };
122+
type GetUsersBody = {};
123+
type GetUsersResponse = { foo?: string };
138124
}
139125
140126
declare class OpenapiClient<T extends object = object> extends BaseOpenapiClient<T> {
@@ -191,17 +177,10 @@ test('完整的类型提示', async () => {
191177
{
192178
"OpenapiClient": {
193179
"dts": "declare namespace OpenapiClient {
194-
interface GetUsersQuery {
195-
foo?: string;
196-
bar?: string;
197-
}
198-
interface GetUsersParams {
199-
baz: number;
200-
}
201-
interface GetUsersBody {}
202-
interface GetUsersResponse {
203-
foo?: string;
204-
}
180+
type GetUsersQuery = { foo?: string; bar?: string };
181+
type GetUsersParams = { baz: number };
182+
type GetUsersBody = {};
183+
type GetUsersResponse = { foo?: string };
205184
}
206185
207186
declare class OpenapiClient<T extends object = object> extends BaseOpenapiClient<T> {
@@ -283,19 +262,10 @@ describe('命名空间', () => {
283262
const result = generateNamespaceTpl('Client', metas);
284263
await expect(formatDocs(result)).resolves.toMatchInlineSnapshot(`
285264
"declare namespace Client {
286-
interface AUsersQuery {
287-
foo: string;
288-
}
289-
interface AUsersParams {
290-
id: number;
291-
}
292-
interface AUsersBody {
293-
bar: string;
294-
}
295-
interface AUsersResponse {
296-
id: number;
297-
name: string;
298-
}
265+
type AUsersQuery = { foo: string };
266+
type AUsersParams = { id: number };
267+
type AUsersBody = { bar: string };
268+
type AUsersResponse = { id: number; name: string };
299269
}
300270
"
301271
`);

test/lib/parse-parameters.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ test('解析查询字符串', () => {
6363
{
6464
"optional": false,
6565
"types": [
66-
"{ foo: number
66+
"{ foo: (number)
6767
;
68-
bar?: number
68+
bar?: (number)
6969
;
70-
ref_a?: number
70+
ref_a?: (number)
7171
;
72-
bazz: string
72+
bazz: (string)
7373
}",
7474
],
7575
}
@@ -82,9 +82,9 @@ test('解析路径参数', () => {
8282
{
8383
"optional": false,
8484
"types": [
85-
"{ id: number
85+
"{ id: (number)
8686
;
87-
ref_b: number
87+
ref_b: (number)
8888
}",
8989
],
9090
}
@@ -127,7 +127,7 @@ test('允许参数内没有schema结构', () => {
127127
{
128128
"optional": false,
129129
"types": [
130-
"{ bar: string
130+
"{ bar: (string)
131131
}",
132132
],
133133
}
@@ -161,9 +161,9 @@ test('所有结构都不是必填时,optional=true', () => {
161161
{
162162
"optional": true,
163163
"types": [
164-
"{ foo?: string
164+
"{ foo?: (string)
165165
;
166-
bar?: string
166+
bar?: (string)
167167
}",
168168
],
169169
}
@@ -198,7 +198,7 @@ test('包含描述', () => {
198198
* foo-bar
199199
* @deprecated
200200
*/
201-
foo?: string
201+
foo?: (string)
202202
}",
203203
],
204204
}

test/lib/parse-request-body.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ test('从requestBody字段获取', () => {
2424
"body": {
2525
"optional": false,
2626
"types": [
27-
"{ foo?: string }",
27+
"({ foo?: (string) })",
2828
],
2929
},
3030
"contentTypes": [
@@ -57,8 +57,8 @@ test('多个返回类型', () => {
5757
"body": {
5858
"optional": false,
5959
"types": [
60-
"{ foo?: string }",
61-
"string",
60+
"({ foo?: (string) })",
61+
"(string)",
6262
],
6363
},
6464
"contentTypes": [
@@ -138,7 +138,7 @@ test('*/* 类型被忽略', () => {
138138
"body": {
139139
"optional": false,
140140
"types": [
141-
"{ foo?: string }",
141+
"({ foo?: (string) })",
142142
],
143143
},
144144
"contentTypes": [],

test/lib/parse-response.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ test('从response字段获取', () => {
2424
{
2525
"response": {
2626
"types": [
27-
"{ foo?: string }",
27+
"({ foo?: (string) })",
2828
],
2929
},
3030
"responseTypes": [
@@ -72,8 +72,8 @@ test('只保留2xx状态', () => {
7272
{
7373
"response": {
7474
"types": [
75-
"{ foo?: string }",
76-
"{ foo1?: string }",
75+
"({ foo?: (string) })",
76+
"({ foo1?: (string) })",
7777
],
7878
},
7979
"responseTypes": [
@@ -105,7 +105,7 @@ test('*/* 类型被忽略', () => {
105105
{
106106
"response": {
107107
"types": [
108-
"{ foo?: string }",
108+
"({ foo?: (string) })",
109109
],
110110
},
111111
"responseTypes": [],

0 commit comments

Comments
 (0)