swagger-typescript-api icon indicating copy to clipboard operation
swagger-typescript-api copied to clipboard

Missing 'type' keyword in generated files when importing from data-contracts

Open JasonLandbridge opened this issue 2 years ago • 6 comments

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: image

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.

JasonLandbridge avatar Aug 09 '23 08:08 JasonLandbridge

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?

srad avatar Feb 29 '24 17:02 srad

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.

Nagell avatar Apr 16 '24 08:04 Nagell

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();

catgoose avatar May 13 '24 21:05 catgoose

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

catgoose avatar May 14 '24 11:05 catgoose