Missing 'type' keyword in generated files when importing from data-contracts
Hi there,
With the following command: npx swagger-typescript-api -p <SWAGGER URL> --responses --modular --axios --unwrap-response-data --route-types --output ./src/api/types/ --name api-types.ts
The following correct files are generated:
However, inside ConsultRequests.ts the types from data-contracts.ts are imported but should be prefixed with type
// ConsultRequests.ts
import {
CommunicationMessage,
CommunicationMessageOptional,
ConsultRequest,
ConsultRequestListCollection,
InvalidContentTypeProblemDetails,
InvalidRequestBodyProblemDetails,
Item,
ItemHistory,
ProblemDetails,
} from "./data-contracts";
When running vue-tsc, which Typescript checks the project for type errors:
vue-tsc --noEmit
src/api/types/ConsultRequests.ts:12:1 - error TS1371: This import is never used as a value and must use 'import type' because 'importsNotUsedAsValues' is set to 'error'.
12 import {
~~~~~~~~
13 CommunicationMessage,
~~~~~~~~~~~~~~~~~~~~~~~
...
21 ProblemDetails,
~~~~~~~~~~~~~~~~~
22 } from "./data-contracts";
~~~~~~~~~~~~~~~~~~~~~~~~~~
src/api/types/ConsultRequests.ts:13:3 - error TS1444: 'CommunicationMessage' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
13 CommunicationMessage,
~~~~~~~~~~~~~~~~~~~~
src/api/types/ConsultRequests.ts:14:3 - error TS1444: 'CommunicationMessageOptional' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
14 CommunicationMessageOptional,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/api/types/ConsultRequests.ts:15:3 - error TS1444: 'ConsultRequest' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
15 ConsultRequest,
~~~~~~~~~~~~~~
src/api/types/ConsultRequests.ts:16:3 - error TS1444: 'ConsultRequestListCollection' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
16 ConsultRequestListCollection,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/api/types/ConsultRequests.ts:17:3 - error TS1444: 'InvalidContentTypeProblemDetails' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
17 InvalidContentTypeProblemDetails,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/api/types/ConsultRequests.ts:18:3 - error TS1444: 'InvalidRequestBodyProblemDetails' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
18 InvalidRequestBodyProblemDetails,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/api/types/ConsultRequests.ts:19:3 - error TS1444: 'Item' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
19 Item,
~~~~
src/api/types/ConsultRequests.ts:20:3 - error TS1444: 'ItemHistory' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
20 ItemHistory,
~~~~~~~~~~~
src/api/types/ConsultRequests.ts:21:3 - error TS1444: 'ProblemDetails' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
21 ProblemDetails,
~~~~~~~~~~~~~~
src/api/types/ConsultRequests.ts:23:35 - error TS1444: 'RequestParams' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
23 import { ContentType, HttpClient, RequestParams } from "./http-client";
~~~~~~~~~~~~~
src/api/types/Statistics.ts:12:1 - error TS1371: This import is never used as a value and must use 'import type' because 'importsNotUsedAsValues' is set to 'error'.
12 import { ConsultRequestStatistics, ProblemDetails } from "./data-contracts";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/api/types/Statistics.ts:12:10 - error TS1444: 'ConsultRequestStatistics' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
12 import { ConsultRequestStatistics, ProblemDetails } from "./data-contracts";
~~~~~~~~~~~~~~~~~~~~~~~~
src/api/types/Statistics.ts:12:36 - error TS1444: 'ProblemDetails' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
12 import { ConsultRequestStatistics, ProblemDetails } from "./data-contracts";
~~~~~~~~~~~~~~
src/api/types/Statistics.ts:13:22 - error TS1444: 'RequestParams' is a type and must be imported using a type-only import when 'preserveValueImports' and 'isolatedModules' are both enabled.
13 import { HttpClient, RequestParams } from "./http-client";
~~~~~~~~~~~~~
Workaround
I can disable/remove --modular from the command, and then all the Typescript errors are resolved.
This is a bug, combined with the tsconfig.json setting "verbatimModuleSyntax": true. When you set it to false, the error will go away. However, someone forgot/did not update the correlation between --modular parameter and "verbatimModuleSyntax": true, Any updates?
It would be very helpful to have this option. without it we are forced to add id manually every time after generation.
Is it maybe solvable with templates? I tried it, but I cannot find out how do they work.
I fixed this by editing route-types.ejs.
The line that contains import type { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>" I added type
<%
const { utils, config, route, modelTypes } = it;
const { _, pascalCase } = utils;
const { routes, moduleName } = route;
const dataContracts = config.modular ? _.map(modelTypes, "name") : [];
%>
<% if (dataContracts.length) { %>
import type { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>"
<% } %>
export namespace <%~ pascalCase(moduleName) %> {
<% for (const route of routes) { %>
<%~ includeFile('./route-type.ejs', { ...it, route }) %>
<% } %>
}
I also edited api.ejs:
Edited these lines:
import type { HttpClient, RequestParams, ContentType, HttpResponse } from "./<%~ config.fileNames.httpClient %>";
<% if (dataContracts.length) { %>
import type { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>"
<% } %>
<%
const { utils, route, config, modelTypes } = it;
const { _, pascalCase, require } = utils;
const apiClassName = pascalCase(route.moduleName);
const routes = route.routes;
const dataContracts = _.map(modelTypes, "name");
%>
<% if (config.httpClientType === config.constants.HTTP_CLIENT.AXIOS) { %> import type { AxiosRequestConfig, AxiosResponse } from "axios"; <% } %>
import type { HttpClient, RequestParams, ContentType, HttpResponse } from "./<%~ config.fileNames.httpClient %>";
<% if (dataContracts.length) { %>
import type { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>"
<% } %>
export class <%= apiClassName %><SecurityDataType = unknown><% if (!config.singleHttpClient) { %> extends HttpClient<SecurityDataType> <% } %> {
<% if(config.singleHttpClient) { %>
http: HttpClient<SecurityDataType>;
constructor (http: HttpClient<SecurityDataType>) {
this.http = http;
}
<% } %>
<% for (const route of routes) { %>
<%~ includeFile('./procedure-call.ejs', { ...it, route }) %>
<% } %>
}
I'm using this node script:
import * as path from 'path';
import sta from 'swagger-typescript-api';
const { generateApi, generateTemplates } = sta;
const config = {
generateApi: true,
generateTemplates: false,
};
async function genTemplates() {
await generateTemplates({
cleanOutput: false,
output: path.resolve('swagger/templates'),
httpClientType: 'fetch',
modular: true,
silent: false,
rewrite: true,
});
}
async function genApi() {
await generateApi({
debug: false,
name: 'HomeSheetsApi.ts',
apiClassName: 'HomeSheetsApi',
output: path.resolve('src/api'),
url: 'https://homesheets-dev.dsldhomes.com/swagger/v1/swagger.json',
templates: path.resolve('swagger/templates'),
generateClient: true,
httpClientType: 'fetch',
modular: true,
cleanOutput: true,
defaultResponseAsSuccess: false,
generateRouteTypes: false,
generateResponses: true,
toJS: false,
extractRequestParams: true,
extractRequestBody: true,
extractEnums: true,
unwrapResponseData: true,
defaultResponseType: 'void',
singleHttpClient: true,
enumNamesAsValues: true,
moduleNameFirstTag: true,
generateUnionEnums: false,
typePrefix: '',
typeSuffix: '',
enumKeyPrefix: '',
enumKeySuffix: '',
addReadonly: false,
sortTypes: true,
sortRouters: true,
extractingOptions: {
requestBodySuffix: ['Payload', 'Body', 'Input'],
requestParamsSuffix: ['Params'],
responseBodySuffix: ['Data', 'Result', 'Output'],
responseErrorSuffix: ['Error', 'Fail', 'Fails', 'ErrorData', 'HttpError', 'BadResponse'],
},
extraTemplates: [],
anotherArrayType: false,
fixInvalidTypeNamePrefix: 'Type',
fixInvalidEnumKeyPrefix: 'Value',
primitiveTypeConstructs: (constructs) => ({
...constructs,
string: {
'date-time': 'Date',
},
}),
});
}
if (config.generateApi) {
try {
await genApi();
} catch (e) {
console.log(e);
}
}
if (config.generateTemplates) {
try {
await genTemplates();
} catch (e) {
console.log(e);
}
}
// eslint-disable-next-line
process.exit();
Looks like I had to make one more change in api.ejs:
import type { HttpClient, RequestParams, HttpResponse } from "./<%~ config.fileNames.httpClient %>";
import { ContentType } from './http-client'
This resolves the error about using a type import as a value