递归获取 interface 中指定类型
interface Path {
"/v1/user/update": {
post: {
a: string;
response: {
"200": {
data: string;
};
};
};
};
}
type GetResponseType<T extends keyof Path, K extends keyof Path[T]> = K extends string
? Path[T][K]
: never;
type GetPathType<T extends keyof Path, P extends string[]> = P extends [infer Head, ...infer Tail]
? Head extends keyof Path
? GetPathType<Path[Head], Tail>
: never
: T extends keyof Path
? Path[T]
: never;
function getPath<T extends keyof Path, P extends string[]>(path: T, ...keys: P): GetPathType<T, P> {
return keys.reduce((acc, key) => acc[key], path) as GetPathType<T, P>;
}
const pathType: GetPathType<"/v1/user/update", ["post", "response", "200"]> = {
data: 'example',
};
console.log(pathType);
const specificResponseType: GetResponseType<"/v1/user/update", "200"> = {
data: 'example',
};
console.log(specificResponseType);
const dynamicPathType = getPath("/v1/user/update", "post", "response", "200");
console.log(dynamicPathType);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
实现
const fs = require('fs');
const axios = require('axios');
const { spawn } = require('child_process');
const command = 'npx';
const args = ['openapi-typescript', './testDemo/openapi.json', '-o', './testDemo/schema.d.ts'];
const outputDir = './testDemo';
console.log('Command executing......');
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir);
console.log(`Folder "${outputDir}" created.`);
}
async function fetchSwagger() {
const url = 'swagger文档地址';
const res = await axios.get(url);
const data = JSON.stringify(res.data);
fs.writeFile(`./${outputDir}/openapi.json`, data, (err) => {
if (err) {
console.error('Failed to generate file:', err);
} else {
console.log('File generated successfully.');
const process = spawn(command, args, { stdio: 'inherit' });
process.on('close', (code) => {
if (code === 0) {
console.log('Command executed successfully.');
collectTagsAndGenerateCode(res.data);
} else {
console.error(`Command failed with code ${code}.`);
}
});
}
});
}
async function collectTagsAndGenerateCode(apiJson) {
const tagsMap = collectTags(apiJson);
for (const modelKey in tagsMap) {
const model = tagsMap[modelKey];
const axiosFunctionsContent = generateAxiosFunctions(model.name, model.paths);
if (fs.existsSync(outputDir)) {
fs.writeFile(`./${outputDir}/${model.name}.ts`, axiosFunctionsContent, (err) => {
if (err) {
console.error('Failed to generate file:', err);
} else {
console.log('File generated successfully.');
}
});
} else {
console.log(`Folder "${outputDir}" created.`);
}
}
}
function generateAxiosFunctions(modelName, apiPaths) {
let axiosFunctions = '';
const header = 'import axios from "../src/service/tools.ts" \n';
const dts = "import type {paths} from '../schema.d.ts' \n";
const ReturnValidType = `type ReturnValidType = T[keyof T] extends { content: infer D }
? D extends { '*/*': infer U }
? U
: unknown
: unknown;
`;
axiosFunctions += header;
axiosFunctions += dts;
axiosFunctions += ReturnValidType;
for (const path of apiPaths) {
const method = path.method;
const url = path.path;
const tags = '';
const axiosFunction = generateAxiosFunction(url, method, tags);
axiosFunctions += axiosFunction + '\n\n';
}
return axiosFunctions;
}
function generateFunctionName(url) {
const parts = url.split('/');
const functionName = parts
.filter((part) => part !== '')
.map((part) => part.replace(/[{}]/g, ''))
.map((part) => part.replace(/(\b\w)/gi, (match) => match.toUpperCase()))
.join('');
return functionName;
}
function generateAxiosFunction(url, method, tags) {
const functionName = generateFunctionName(url);
return `
export type IProps_${functionName} = paths['${url}']['${method}']['requestBody']['content']['application/json']['data']
// export type IRes_${functionName}_prefix =paths['${url}']['${method}']['responses'][200]['content']['*/*']
export type IRes_${functionName}_prefix =paths['${url}']['${method}']['responses']
export type IRes_${functionName} =ReturnValidType${functionName}_prefix>
export async function ${functionName}(requestData: IProps_${functionName}) {
try {
const response = await axios({
method: '${method}',
url: '${url}',
data: requestData
});
console.log('API response:', response.data);
return response.data as IRes_${functionName} ;
} catch (error) {
console.error('API error:', error);
throw error;
}
}
`;
}
function collectTags(apiJson) {
const tagsMap = {};
const tags = apiJson.tags;
const paths = apiJson.paths;
for (const pathKey in paths) {
const path = paths[pathKey];
for (const method in path) {
const pathTags = path[method]?.tags || [];
pathTags.forEach((tag) => {
const item = {
[pathKey]: {
[method]: path[method],
},
method,
path: pathKey,
};
if (tagsMap[tag]) {
tagsMap[tag].paths.push(item);
} else {
tagsMap[tag] = {
name: tag,
paths: [item],
};
}
});
}
}
return tagsMap;
}
fetchSwagger();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177