lukeautry / tsoa

Build OpenAPI-compliant REST APIs using TypeScript and Node
MIT License
3.38k stars 484 forks source link

Use "typeof xyz" as request / response types #737

Open bkniffler opened 4 years ago

bkniffler commented 4 years ago

Expected Behavior

I'd like to use typeof based request / response types, since I'm using the input / output objects for runtime validation purposes and I'd like to keep the code as simple as possible. Check below code for an example.

ValidationMapper is a little helper I've attached to convert objects like { name: "?string" } to types like { name: string | undefined }

import { Body, Controller, Post, Route, SuccessResponse, Response } from 'tsoa';

@Route('invoke')
export class InvokeController extends Controller {
  @SuccessResponse('201', 'Created') // Custom success response
  @Response('500', 'Error') // Custom success response
  @Post()
  public async post(
    @Body() requestBody: ValidationMapper<typeof input>
  ): Promise<ValidationMapper<typeof output>> {
    return undefined;
  }
}

// Input type
const input: ValidationObject = {
  name: 'string',
};

// Input type
const output: ValidationObject = {
  name: 'string',
};

// Validation helpers
type ValidationFieldTypeMapRequired = {
  'string[]': string[];
  string: string;
  boolean: boolean;
  number: number;
  any: any;
};
type ValidationFieldTypeMapOptional = {
  '?string[]': string[] | undefined | null | never;
  '?string': string | undefined | null | never;
  '?boolean': boolean | undefined | null | never;
  '?number': number | undefined | null | never;
  '?any': any | undefined | null | never;
};
type ValidationFieldTypeMap = {} & ValidationFieldTypeMapOptional &
  ValidationFieldTypeMapRequired;
type ValidationObject = {
  [s: string]: keyof ValidationFieldTypeMap;
};
type ValidationMapper<T extends ValidationObject> = {
  [K in keyof T]: ValidationFieldTypeMap[T[K]];
};

Current Behavior

Error:

Generate routes error.
 Error: Unknown type: TypeQuery
At: generated/controllers/invoke.ts:10:10.
This was caused by 'Promise<typeof output>'
    at new GenerateMetadataError (/tsoa/node_modules/tsoa/dist/metadataGeneration/exceptions.js:22:28)
    at TypeResolver.resolve (/tsoa/node_modules/tsoa/dist/metadataGeneration/typeResolver.js:286:19)
    at TypeResolver.resolve (/tsoa/node_modules/tsoa/dist/metadataGeneration/typeResolver.js:309:118)
    at MethodGenerator.Generate (/tsoa/node_modules/tsoa/dist/metadataGeneration/methodGenerator.js:55:76)
    at /Users/bkniffler/Git/growth-hacking-api/tsoa/node_modules/tsoa/dist/metadataGeneration/controllerGenerator.js:59:58
    at Array.map (<anonymous>)
    at ControllerGenerator.buildMethods (/tsoa/node_modules/tsoa/dist/metadataGeneration/controllerGenerator.js:59:14)
    at ControllerGenerator.Generate (/tsoa/node_modules/tsoa/dist/metadataGeneration/controllerGenerator.js:48:27)
    at /Users/bkniffler/Git/growth-hacking-api/tsoa/node_modules/tsoa/dist/metadataGeneration/metadataGenerator.js:145:58
    at Array.map (<anonymous>)
WoH commented 4 years ago

2 Quick notes: 1) Yes, we should add type operators, probably requires work with the TypeChecker. 2) We already validate at runtime, as we compile TS Types into JSON-Schema(ish) metadata we use for validation, so you don't need to duplicate here probably.

bkniffler commented 4 years ago

Thanks for pointing out! Unfortunately it doesn't really fit well into my particular workflow where I'm using Lambda and got a core library that's shared by multiple lambdas to do the validation according to the types I mentioned above. I know that tsoa has a generator with handlebars, but I think this would make my stack more complicated instead of easier. Would love to use tsoa solely to generate a doc for all my lambdas and the above problem is the only one that's holding me back.