Generating a model from a swagger file only creates interfaces and no classes.
Creating manually classes for a complex model can be a hassle.
Master branch : 2.3.0
bash
java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \
-i ./my-swagger.yaml \
-l typescript-angular2 \
-o samples/client/my-client/typescript/my-client-api
Generate a client in typescript-angular2 from any swagger file.
Is it planned to generate classes – with constructor, getters and setters – implementing the generated interfaces to help with client-side object manipulation ?
I know it won't be of use in a lot of cases, maybe allow classes generation with a command parameter ?
Default generated file :
https://gist.github.com/pierredadt/d04f78ad06250b6f26247753ccbe7edd
Evolution proposed :
https://gist.github.com/pierredadt/d3ed8273079b62f7ed7110434a89251c
cc @TiFu @taxpon @sebastianhaas @kenisteward @Vrolijkx
@pierredadt What do you mean by complex classes? None of the things generated have functions tied to them as they are just a representation of what data will be received and sent.
This is a feature that really isn't needed. If you think about it generating the classes give you nothing in return for extra work.
E.g. I'm assuming you want to do
const values: Metadata = { severity: 1, occurance: 5 }
const obj = new Metadata(values);
That is no different from doing
const obj: Metadata = {
occurance: 5,
severity: 1
}
All of the popular IDE's give you all the intellisense you need to easily set these values. The second line is the way that most in the TS/JS world set values that simply represent data to be sent/received. Classes really are only needed when you have extra functions that might manipulate data or do things other than getting and setting values.
I think this has also been discussed in an issue before and it was ultimately turned down as TS/JS aren't your normal strongly typed languages. Interfaces are just a better fit for data this simple.
If we were to implement this, however, it would definitely be flag based like you suggested.
@TiFu @taxpon @sebastianhaas @Vrolijkx @wing328 I'd love for you guys to chime in on this as I think that interfaces are the best route for defining the data structures and classes are not needed in this type of client environment
Yeah, I totally agree with what @kenisteward said. We've had this discussion a couple of times in different places. Last time I've checked (don't mean to be sarcastic, TS changes a lot), classes in TS are compiled into some JS stub, while interfaces just disappear- which is what we really want here, we just want some additional type safety with no extra cost during runtime.
That being said and since this question is tagged with Angular, I also believe that having business logic in your models (and I don't see no other reason for implementing classes really), is probably not what Angular suggests you to do.
What about generating model validation code, based on the OpenApi specification: required fields, min- & max-length... how do you suggest we should do that? I would have thought to generate that into the model classes.
The one thing I will argue (although it's a luxury item) is that having things as classes opposed to interfaces makes debugging easier.
Example:
interface IPerson {
firstName: string;
lastName: string;
}
class Person {
public firstName: string;
public lastName: string;
constructor(person: IPerson) {
Object.assign(this, person);
}
}
const person: IPerson = { firstName: 'John', lastName: 'Doe' };
console.log(new Person(person)); // <-- easily identifiable object in console
console.log(person); // <-- not easily identifiable object in console
Output:

Essentially, we can do this still. We just need to wrap every single call to our generated services (the http calls) and when they finish we would need to new up the models on our side.
Example:
this.userSerivce.getPerson(1).pipe( map(p => new Person(p) ) );
I agree with the feature request but also agree with the push back. I would like this feature to happen, but I have no idea how it would actually work. To the points above, you won't get any extra benefit if the generated swagger returns a class over an interface (except for the nice console messages described above).
In reality, if you're going to use a class in TypeScript, that means you require some further logic on that model. It would be FREAKING AWESOME if there was some sort of attribute or identifier that could be put on the back-end code to flag it as "this method will convert gracefully with JavaScript". An example of this type of method would be:
[SwaggerJavaScriptClassMethodThatWillConvert]
public string GetFullName()
{
return string.Concat(this.FirstName, this.LastName);
}
I just don't see how that would ever work.
Being a full stack guy, I'm pretty damn happy with the fact my front end services and interfaces are generated. That saves sooooooooo much time. Managing two different models and endpoint consumer and producer is extremely time consuming.
With that being said, I think the current workaround to getting your generated models converted over to classes is decent, but it would be great if there were some improvements
Current workaround I use:
NOTE: I don't like how you have redeclare your properties you're inheriting, but that's more of a typescript issue than a swagger issue. With that being said, it is an extra layer of overhead.
/**
* IPerson is generated from swagger-codegen
*/
class Person implements IPerson {
public firstName: string;
public lastName: string;
constructor( person: IPerson ) {
Object.assign( this, person );
// if there were complex classes, you could handle them after Object.assign
// Imagine a Department class or something
// this.department = new Department(person.department);
}
}
Then, whenever I call my server, I just use rxjs map
this.userSerivce.getPerson().pipe( map(p => new Person(p) ) );
Not the best, but I really don't see any other option if you really need your interface to be a class.
Why do you have to manage two different models??
If you use node you just import the same interfaces and go right?
On Sat, Dec 14, 2019, 8:04 PM Marc notifications@github.com wrote:
The one thing I will argue (although it's a luxury item) is that having
things as classes opposed to interfaces makes debugging easier.Example:
interface IPerson {
firstName: string;
lastName: string;
}
class Person {
public firstName: string;
public lastName: string;
constructor(person: IPerson) {
Object.assign(this, person);
}
}const person: IPerson = { firstName: 'John', lastName: 'Doe' };
console.log(new Person(person)); // <-- easily identifiable object in console
console.log(person); // <-- not easily identifiable object in consoleOutput:
[image: image]
https://user-images.githubusercontent.com/19480392/70856852-cb20ad00-1e98-11ea-869e-309978156a3c.pngEssentially, we can do this still. We just need to wrap every single call
to our generated services (the http calls) and when they finish we would
need to new up the models on our side.Example:
this.userSerivce.getPerson(1).pipe( map(p => new Person(p) ) );I agree with the feature request but also agree with the push back. I
would like this feature to happen, but I have no idea how it would actually
work. To the points above, you won't get any extra benefit if the generated
swagger returns a class over an interface (except for the nice console
messages described above).In reality, if you're going to use a class in TypeScript, that means you
require some further logic on that model. It would be FREAKING AWESOME
if there was some sort of attribute or identifier that could be put on the
back-end code to flag it as "this method will convert gracefully with
JavaScript". An example of this type of method would be:[SwaggerJavaScriptClassMethodThatWillConvert] public string GetFullName() { return string.Concat(this.FirstName, this.LastName); }I just don't see how that would ever work.
Being a full stack guy, I'm pretty damn happy with the fact my front end
services and interfaces are generated. That saves sooooooooo much time.
Managing two different models and endpoint consumer and producer is
extremely time consuming.With that being said, I think the current workaround to getting your
generated models converted over to classes is decent, but it would be great
if there were some improvementsCurrent workaround I use:
NOTE: I don't like how you have redeclare your properties you're
inheriting, but that's more of a typescript issue than a swagger issue.
With that being said, it is an extra layer of overhead./**
- IPerson is generated from swagger-codegen
*/
class Person implements IPerson {
public firstName: string;
public lastName: string;
constructor( person: IPerson ) {
Object.assign( this, person );
// if there were complex classes, you could handle them after Object.assign
// Imagine a Department class or something
// this.department = new Department(person.department);
}
}Then, whenever I call my server, I just use rxjs map
this.userSerivce.getPerson().pipe( map(p => new Person(p) ) );
Not the best, but I really don't see any other option if you really need
your interface to be a class.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/swagger-api/swagger-codegen/issues/6546?email_source=notifications&email_token=ADB4XNPGNKZIBWGEGV7LMZDQYWGB5A5CNFSM4D4CUDGKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEG4PP3I#issuecomment-565770221,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/ADB4XNP2KMBB6RHRG6AI73LQYWGB5ANCNFSM4D4CUDGA
.
@kenisteward If you were to not use swagger to generate your typescript/angular services and modesl you would have the following setup:
1.) Backend methods for HTTP calls (GET, POST, PATCH, etc...)
2.) Backend data contracts/models for the inputs of those methods/endpoints
on front end
1.) You have to create a service leveraging HttpClient to actually call your back end methods
2.) Front end data contracts/models that match your backend models
Obviously you could just not do the models of the front end and just exclusively work with JavaScript objects, but I would argue that goes against the entire point of TypeScript.
Using swagger, you can bypass all of the front end steps since that's all generated for you from the backend. It saves _a lot_ of time.
@mswilson4040 it sounds like if it's already saved lots of time there is no need to save extra. For those who use codegen only to test api with mocha + chai it's still a small overhead to replace all instances of 'interface' to 'type' but it'd be nice to have such an option from the box.
I would agree. Given the fact that there is no way to carry over any class methods written in the backend and have them be consumed in the front end, there's no point in delivering a class.
I would recommend keeping them as interfaces and not types. Maybe a flag for it, but not the default. Definitely not the default. Those are interfaces.
For those who really need their interface to be a class, they can easily use TypeScripts Declaration Merging to set it up.
in case to save some time to searchers - following line make types from interfaces:
find . -name *.ts -exec sed -i -e "s/interface/type/g" -e "s/ { \$/ = { /g" {} \;
Most helpful comment
Yeah, I totally agree with what @kenisteward said. We've had this discussion a couple of times in different places. Last time I've checked (don't mean to be sarcastic, TS changes a lot), classes in TS are compiled into some JS stub, while interfaces just disappear- which is what we really want here, we just want some additional type safety with no extra cost during runtime.
That being said and since this question is tagged with Angular, I also believe that having business logic in your models (and I don't see no other reason for implementing classes really), is probably not what Angular suggests you to do.