递归获取 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);
实现
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> = 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<IRes_${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();