RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.69k stars 1.24k forks source link

Typescript generated interfaces contain class type properties #4165

Open vocko opened 1 year ago

vocko commented 1 year ago

I'm not sure whether this is intended or whether it is my setup, but for each type that is a part of an inheritance chain, the member property is class typed not interface typed.

Example:

interface ITypeA {
    propA: INonInheritedType;  // this is generated as an interface type
    propB: InheritedType;      // <-- I can't get this generated as an interface type 
}

interface IInheritedType extends IParentType {
    // properties here
}

class InheritedType extends ParentType {
    // implementation here
}

interface INonInheritedType {
    // properties here
}

The Nswag.json settings generateConstructorInterface and convertConstructorInterfaceData seem to be somehow involved in the conversion process of most of the properties into interface types, however, the inherited types persist to be class typed. And I can't find any documentation what are these switches meant to do or how do they work.

Having the class typed member properties causes issues in places where you need to work with plain objects, like redux toolkit library for example.

My nswag.json looks like follows:

{
  "runtime": "Net60",
  "defaultVariables": null,
  "documentGenerator": {
    "aspNetCoreToOpenApi": {
      "project": "ProjectName.csproj",
      "msBuildProjectExtensionsPath": null,
      "configuration": "$(Configuration)",
      "runtime": null,
      "targetFramework": null,
      "noBuild": true,
      "verbose": true,
      "workingDirectory": null,
      "requireParametersWithoutDefault": false,
      "apiGroupNames": null,
      "defaultPropertyNameHandling": "Default",
      "defaultReferenceTypeNullHandling": "Null",
      "defaultDictionaryValueReferenceTypeNullHandling": "NotNull",
      "defaultResponseReferenceTypeNullHandling": "NotNull",
      "defaultEnumHandling": "Integer",
      "flattenInheritanceHierarchy": false,
      "generateKnownTypes": true,
      "generateEnumMappingDescription": false,
      "generateXmlObjects": false,
      "generateAbstractProperties": false,
      "generateAbstractSchemas": true,
      "ignoreObsoleteProperties": false,
      "allowReferencesWithProperties": false,
      "excludedTypeNames": [],
      "serviceHost": null,
      "serviceBasePath": null,
      "serviceSchemes": [],
      "infoTitle": "My Title",
      "infoDescription": null,
      "infoVersion": "1.0.0",
      "documentTemplate": null,
      "documentProcessorTypes": [],
      "operationProcessorTypes": [],
      "typeNameGeneratorType": null,
      "schemaNameGeneratorType": null,
      "contractResolverType": null,
      "serializerSettingsType": null,
      "useDocumentProvider": true,
      "documentName": "v1",
      "aspNetCoreEnvironment": null,
      "createWebHostBuilderMethod": null,
      "startupType": null,
      "allowNullableBodyParameters": true,
      "output": "docs/api-specification.json",
      "outputType": "OpenApi3",
      "newLineBehavior": "Auto",
      "assemblyPaths": [],
      "assemblyConfig": null,
      "referencePaths": [],
      "useNuGetCache": false
    }
  },
  "codeGenerators": {
    "openApiToTypeScriptClient": {
      "className": "{controller}Api",
      "moduleName": "",
      "namespace": "",
      "typeScriptVersion": 3.6,
      "template": "Fetch",
      "promiseType": "Promise",
      "httpClass": "Http",
      "withCredentials": false,
      "useSingletonProvider": false,
      "injectionTokenType": "OpaqueToken",
      "rxJsVersion": 6.3,
      "dateTimeType": "Date",
      "nullValue": "Undefined",
      "generateClientClasses": true,
      "generateClientInterfaces": false,
      "generateOptionalParameters": false,
      "exportTypes": true,
      "wrapDtoExceptions": false,
      "exceptionClass": "SwaggerException",
      "clientBaseClass": "BaseApi",
      "wrapResponses": false,
      "wrapResponseMethods": [],
      "generateResponseClasses": true,
      "responseClass": "SwaggerResponse",
      "protectedMethods": [],
      "configurationClass": null,
      "useTransformOptionsMethod": true,
      "useTransformResultMethod": true,
      "generateDtoTypes": true,
      "operationGenerationMode": "MultipleClientsFromOperationId",
      "markOptionalProperties": true,
      "generateCloneMethod": false,
      "typeStyle": "Class",
      "enumStyle": "Enum",
      "useLeafType": false,
      "classTypes": [],
      "extendedClasses": [],
      "extensionCode": "src/global/BaseApi.ts",
      "generateDefaultValues": true,
      "excludedTypeNames": [],
      "excludedParameterNames": [],
      "handleReferences": false,
      "generateConstructorInterface": true,
      "convertConstructorInterfaceData": true,
      "importRequiredTypes": true,
      "useGetBaseUrlMethod": false,
      "baseUrlTokenName": "API_BASE_URL",
      "queryNullValue": "",
      "inlineNamedDictionaries": false,
      "inlineNamedAny": false,
      "templateDirectory": "Nswag/Templates",
      "typeNameGeneratorType": null,
      "propertyNameGeneratorType": null,
      "enumNameGeneratorType": null,
      "serviceHost": null,
      "serviceSchemes": null,
      "output": "src/global/Api.generated.ts",
      "newLineBehavior": "Auto"
    }
  }
}

Any help will be highly appreciated.

PeterKottas commented 1 year ago

Did you manage to figure out how to solve this?

vocko commented 1 year ago

Did you manage to figure out how to solve this?

Nope. We have decided to switch to interface mode as the class model was giving us too many headaches. But would be still good to resolve this one.

PeterKottas commented 1 year ago

Yep, same here.