spot icon indicating copy to clipboard operation
spot copied to clipboard

Support generics

Open lfportal opened this issue 3 years ago • 3 comments

I also noticed the following which capability matrix: union type -> ✅ inheritance type -> ✅ recursive -> 🔴 generics -> 🔴

Would love to hear any thoughts you have on implementing these.

Originally posted by @mahirk in https://github.com/airtasker/spot/issues/1034#issuecomment-699204533

lfportal avatar Oct 22 '20 10:10 lfportal

interface Generic<T> {
  genericProp: T;
}

type A = Generic<String>

Off the top of my head:

  1. Parse type parameters (T)
  2. Parse the generic interface, passing in the resolved type parameters

Questions that need answering:

  • Do we store the resulting type in the typeTable?
    • I'm thinking we don't, and treat it as if it were defined inline (i.e. type A = { genericProp: String; }) to keep it simple
    • If we do, the need some way to name the type. GenericString, GenericInt? But this could lead to type name clashes

Thoughts @mahirk?

https://github.com/airtasker/spot/issues/1034#issuecomment-704566174

Just to confirm we will store Generic in the typeTable regardless of we storing the resulting types, right?

inline type works great when the Generic object is small, but when its big and frequently referenced, the cost of expanding it every where goes up very quickly. The name clashes should not be a problem if we just use the full name Generic<String>. Maybe we can have a threshold on when we should put it into typeTable?

https://github.com/airtasker/spot/issues/1034#issuecomment-713305288 @Noeyfan

The type table is used to generate the components section of OpenAPI documents. We would require some strategy to convert Generic<String> into something OpenAPI friendly. Whatever that becomes will be subject to name clashing with user defined types (this will be true for typescript's utility types too). I think we should aim to avoid any implicit naming, and stick with a clear and predictable syntax. Currently the type table only stores types declared using type aliases and interfaces. I don't see any need to complicate this:

body: {
  a: Generic<String>; // inline type, rendered as an inline schema in OpenAPI
  b: MyType; // reference type, rendered as a $ref schema in OpenAPI
}

type MyType = Generic<String> // stored in type table

https://github.com/airtasker/spot/issues/1034#issuecomment-714377270

lfportal avatar Oct 22 '20 10:10 lfportal

+1 for this, an example use case I have would be something like this:

import { api, endpoint, request, response, body } from "@airtasker/spot";

@api({
  name: "My API"
})
class Api {}

@endpoint({
  method: "POST",
  path: "/users"
})
class CreateUser {
  @request
  request(@body body: CreateUserRequest) {}

  @response({ status: 201 })
  response(@body body: StandardResponse<CreateUserResponse>) {}
}

/**
 * A wrapper for all response types
 */
interface StandardResponse<T = undefined> {
  traceId: string;
  message: string;
  data: T
}

interface CreateUserRequest {
  firstName: string;
  lastName: string;
}

interface CreateUserResponse {
  firstName: string;
  lastName: string;
  role: string;
}

ccakes avatar Nov 06 '20 15:11 ccakes

Hi @lfportal, might there still be interest in this issue, I have something WIP that works for my use case. Might I also ask if you have CLA requirements for code contributions. Thanks!

benjaminlgt avatar Aug 10 '23 18:08 benjaminlgt