Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support generic type parameter and return type #27

Closed
DuckMingBrother opened this issue Dec 14, 2016 · 22 comments · Fixed by #461
Closed

Support generic type parameter and return type #27

DuckMingBrother opened this issue Dec 14, 2016 · 22 comments · Fixed by #461

Comments

@DuckMingBrother
Copy link

Return type:
Current only support return Promise<XXX>, when return Promise<Response<XXX>> or Response<XXX>, cli throw error "No matching model found for referenced type T".

Paramter type:
use generic type in parameter can generate definition file, but swagger show parameter's schema is undefined.

Can you support it? thank you.

@lukeautry
Copy link
Owner

I think this can be supported and it's a great idea. I'll look into it soon.

@nenadalm
Copy link
Collaborator

nenadalm commented Jun 1, 2017

I see that support for this was merged, but if I try to use Promise<ResultList<Resource>> I get an error:

$ ./node_modules/.bin/tsoa swagger
There was a problem resolving type of 'T'.
There was a problem resolving type of 'ResultListResource'.
Error: No matching model found for referenced type T

with ResultList being

interface ResultList<T> {
    data: T[],
}

version: tsoa@1.1.8

@judenaveenraj
Copy link

@nenadalm +1 same here

@judenaveenraj
Copy link

Just something i came across while fiddling with the above
The following seems to work:
interface ResultList {
data: T,
}

The following doesn't, gives the above error that @nenadalm mentioned:
interface ResultList {
data: T[],
}
The following doesn't as well
interface ResultList {
data: Array,
}

@AndyGura
Copy link

AndyGura commented Jul 18, 2017

+1 on this! I can't make something like

export interface IListPage<T> {
    pageIndex: number;
    totalPages: number;
    list: T[];
}

And use as result Promise<IListPage<MyCoolItem>>

@amozh-op
Copy link
Contributor

+1, i have the same issue

@AndyGura
Copy link

@lukeautry Hello, anytning on this? Its been sitting there dormant for a while

@fhewitt
Copy link
Contributor

fhewitt commented Nov 15, 2017

+1, same issue here. This seems a particularly important feature for code maintenance.

@ChamNouki
Copy link

ChamNouki commented Apr 10, 2018

Hello, still have a kind a similar issue :

There was a problem resolving type of 'T'.
There was a problem resolving type of 'JsonApiResourceT'.
There was a problem resolving type of 'JsonApiWrapperUserAttributes'.
Generate routes error.
 Error: No matching model found for referenced type T.
    at new GenerateMetadataError (/Users/thibaut/Documents/poc/jsonapi-serializer/node_modules/tsoa/dist/metadataGeneration/exceptions.js:17:28)
    at getModelTypeDeclaration (/Users/thibaut/Documents/poc/jsonapi-serializer/node_modules/tsoa/dist/metadataGeneration/resolveType.js:419:15)
    at getReferenceType (/Users/thibaut/Documents/poc/jsonapi-serializer/node_modules/tsoa/dist/metadataGeneration/resolveType.js:280:25)
    at resolveType (/Users/thibaut/Documents/poc/jsonapi-serializer/node_modules/tsoa/dist/metadataGeneration/resolveType.js:95:25)
    at /Users/thibaut/Documents/poc/jsonapi-serializer/node_modules/tsoa/dist/metadataGeneration/resolveType.js:496:23
    at Array.map (<anonymous>)
    at getModelProperties (/Users/thibaut/Documents/poc/jsonapi-serializer/node_modules/tsoa/dist/metadataGeneration/resolveType.js:462:14)
    at getReferenceType (/Users/thibaut/Documents/poc/jsonapi-serializer/node_modules/tsoa/dist/metadataGeneration/resolveType.js:281:26)
    at resolveType (/Users/thibaut/Documents/poc/jsonapi-serializer/node_modules/tsoa/dist/metadataGeneration/resolveType.js:92:25)
    at resolveType (/Users/thibaut/Documents/poc/jsonapi-serializer/node_modules/tsoa/dist/metadataGeneration/resolveType.js:69:30)

I use tsoa in version 2.1.4.

Here is my controller :

@Route("users")
export class UsersController extends Controller {
    @Get("{id}")
    public async getUser(
        @Path("id") id: number,
        @Query() name?: string
    ): Promise<JsonApiWrapper<UserAttributes>> {
        const user = await UserService.get(id);
        return SerializerService.serialize(user);
    }

    @SuccessResponse("201", "Created")
    @Post()
    public async createUser(
        @Body() requestBody: UserCreationRequest
    ): Promise<JsonApiWrapper<UserAttributes>> {
        const user = await UserService.create(requestBody);
        this.setStatus(201); // set return status 201
        return SerializerService.serialize(user);
    }
}

and my Models :

User.ts

export interface User extends UserAttributes {
    apiId: number;
}

export interface UserAttributes {
    email: string;
    name: Name;
    status?: Status;
    phoneNumbers: string[];
}

export type Status = "Happy" | "Sad";

export interface Name {
    first: string;
    last?: string;
}

export interface UserCreationRequest {
    email: string;
    name: Name;
    phoneNumbers: string[];
}

JsonApiWrapper.ts

export interface JsonApiWrapper<T> {
    data?:
        | JsonApiResource<T>
        | Array<JsonApiResource<T>>
        | JsonApiResourceIdentifier
        | JsonApiResourceIdentifier[]
        | null;
    error?: JsonApiError[];
    meta?: JsonApiMeta;
    jsonapi?: any;
    links?: JsonApiWrapperLinks;
    included?: Array<JsonApiResource<T>>;
}

JsonApiError.ts

export interface JsonApiError {
  id?: string;
  links?: JsonApiErrorLinks;
  status?: string;
  code?: string;
  title?: string;
  detail?: string;
  source?: JsonApiErrorSource;
}

export interface JsonApiErrorSource {
    pointer: string;
    parameter: string;
    meta: JsonApiMeta;
}

JsonApiMeta.ts

export interface JsonApiMeta {
    [key: string]: any;
}

JsonApiResource.ts

export interface JsonApiResource<T> {
    type: string;
    id?: string;
    attributes?: T;
    relationships?: JsonApiRelationships;
    links?: JsonApiResourceLinks;
    meta?: JsonApiMeta;
}

export interface JsonApiResourceIdentifier {
    type: string;
    id: string;
    meta?: any;
}

JsonApiRelationships.ts

export interface JsonApiRelationships {
    [key: string]: JsonApiRelationship;
}

export interface JsonApiRelationship {
    links: JsonApiLinks;
    data: JsonApiResourceIdentifier | JsonApiResourceIdentifier[] | null;
}

JsonApiLinks.ts

export interface JsonApiLinks {
  self?: string | JsonApiLinkObject;
  related?: string | JsonApiLinkObject;
  [key: string]: string | JsonApiLinkObject;
}

export interface JsonApiLinkObject {
  href: string;
  meta: any;
}

export interface JsonApiResourceLinks extends JsonApiLinks {
  self: string | JsonApiLinkObject;
  related: string | JsonApiLinkObject;
}

export interface JsonApiErrorLinks extends JsonApiLinks {
  about: string | JsonApiLinkObject;
}

export interface JsonApiWrapperLinks extends JsonApiLinks {
  first?: string | JsonApiLinkObject;
  last?: string | JsonApiLinkObject;
  prev?: string | JsonApiLinkObject;
  next?: string | JsonApiLinkObject;
}

any update on this issue ?

@ChamNouki
Copy link

ChamNouki commented Apr 11, 2018

So I tried to replace some generic parts of my interface and the following interface replacing JsonApiWrapper seems to work :

JsonApiUser.ts

export interface JsonApiUser {
    data?:
        | JsonApiResource<UserAttributes>
        | Array<JsonApiResource<UserAttributes>>
        | JsonApiResourceIdentifier
        | JsonApiResourceIdentifier[]
        | null;
    error?: JsonApiError[];
    meta?: JsonApiMeta;
    jsonapi?: any;
    links?: JsonApiWrapperLinks;
    included?: Array<JsonApiResource<UserAttributes>>;
}

I think it's the passing on of the generic type to another generic interface that bring on the issue.

@mazswojejzony
Copy link

Bump. Any plans to fix this?

@lukeautry
Copy link
Owner

This is definitely a desirable feature but it's some work to implement it handle all of the cases.

@andriykrasnychuk
Copy link

+1

@dgreene1
Copy link
Collaborator

Full support for generics was added in v2.4.10.

@ryankeener
Copy link
Contributor

@dgreene1 I think this should be re-opened as I'm still seeing the behavior described above.

It can be replicated by replacing

export interface GenericRequest<T> {
name: string;
value: T;
}

with

export interface GenericRequest<T> {
  name: string;
  value: T;
  nested?: GenericNested<T>;
}

export interface GenericNested<T> {
  foo: T;
}

and re-running the tests:

There was a problem resolving type of 'T'.
There was a problem resolving type of 'GenericNestedT'.
There was a problem resolving type of 'GenericRequestTypeAliasModel1'.
There was a problem resolving type of 'GenericRequestGenericRequestTypeAliasModel1'.
There was a problem resolving type of 'TestModel'.

@dgreene1
Copy link
Collaborator

dgreene1 commented Sep 5, 2019

@ryankeener I know you already confirmed that this works, but I wanted to let you know that this fix/feature was just released in v2.5.1. Can you let us know how it worked out for you?

Side note: we're looking for companies that want to be featured in tsoa's readme: #464

@fhewitt
Copy link
Contributor

fhewitt commented Sep 6, 2019

That's absolutely awesome. I love it!

But still have similar issue adding extends

export interface ThingContainerWithTitle extends GenContainer<TheThing> {
    title: string;
}

export interface GenContainer<T> {
    id: string;
    list: T[];
}

And I got

There was a problem resolving type of 'T'.
There was a problem resolving type of 'GenContainer'.
There was a problem resolving type of 'ThingContainerWithTitle'.

@dgreene1
Copy link
Collaborator

dgreene1 commented Sep 6, 2019

@fhewitt would you be so kind as to make a separate issue for that scenario since the non-extended generic scenarios seem to be resolved?

@fhewitt
Copy link
Contributor

fhewitt commented Sep 9, 2019

Can do that! > #467

@mrgoonie
Copy link

mrgoonie commented Feb 20, 2023

Hi @WoH @dgreene1, sorry for digging this but I think this issue isn't fully resolved (or maybe I'm doing something incorrectly).

import { Get, Route } from "tsoa";

interface ResponseObject<T> {
	status: number;
	data: T | any;
}

interface PingEntity {
	message: string;
}

@Route("ping")
export default class PingController {
	@Get()
	async getMessage() {
		const data: PingEntity = { message: "pong" };
		let result: ResponseObject<PingEntity> = { status: 1, data };
		return result;
	}
}

When I try to generate the swagger json it throws this error:

There was a problem resolving type of 'ResponseObject<any>'.
Generate swagger error.
 Error: Debug Failure. False expression: Node must have a real position for this operation

But when I specified the type directly to ResponseObject (instead of using generic type), it will works normally:

interface ResponseObject {
	status: number;
	data: PingEntity;
}

Here is the example repo: https://github.com/mrgoonie/express-swagger-typescript


Temporary workaround solution:

import { Get, Route } from "tsoa";

// no generic type
interface ResponseObject {
	status: number;
	data: any;
}

interface PingEntity {
	message: string;
}

@Route("ping")
export default class PingController {
	@Get()
	async getMessage() {
		const data: PingEntity = { message: "pong" };
		// use type combination instead of generic type
		let result: ResponseObject & { data: PingEntity } = { status: 1, data };
		return result;
	}
}

Cheers,

@janhrastnik
Copy link

@mrgoonie I had the exact same issue today, the problem is that the return type of getMessage() isn't explicitly typed.

@Get()
async getMessage() {
	const data: PingEntity = { message: "pong" };
	let result: ResponseObject<PingEntity> = { status: 1, data };
	return result;
}

Should be changed to:

@Get()
async getMessage(): Promise<ResponseObject<PingEntity>> {
	const data: PingEntity = { message: "pong" };
	let result: ResponseObject<PingEntity> = { status: 1, data };
	return result;
}

@douglasg14b
Copy link

I don't think it's fully resolved, using a generic that extends a type results in the generic type itself erroring out with GenerateMetadataError: No matching model found for referenced type TKey

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.