typia icon indicating copy to clipboard operation
typia copied to clipboard

Support for Generic Types

Open jarredkenny opened this issue 1 year ago • 4 comments

I am very interested in Application Transformer, and converting TS types into json-schema/openapi.

I would like to be able to use TSON.application with a generic like so:

function toJsonSchema<T> {
  return TSON<T, "swagger",  "#/components/schemas">
}

It would be amazing if these generic references were unrolled at compile time.

However, today this throws Error: Error on TSON.application(): non-specified generic argument(s). and is not supported.

I am very new to typescript transformers so forgive my ignorance. Is this a limitation of transformers or something that just is not supported yet?

I've just come across this project that appears to resolve generic types via a transformer, https://github.com/Hookyns/tst-reflect

jarredkenny avatar Jun 29 '22 10:06 jarredkenny

It looks interesting.

The reason why what I'd blocked non-generic-argumented function call is, typescript-json is generating optimized code by getting type info from the generic argument specification. I don't know exact principle of tst-reflect, how it specializes target type of non-generic-argumented function, but need study.

https://github.com/samchon/typescript-json/blob/3dfbeb097fdd6ea48e060844052aaa4d11274149/src/transformers/features/StringifyTransformer.ts#L14-L19

samchon avatar Jul 01 '22 02:07 samchon

I'll try this after #119.

If you can't wait 2 weeks later, solving the problem and sending PR would be good choice.

samchon avatar Jul 02 '22 12:07 samchon

Any chance to get this? My use-case is that I want to run the type tests in the same test suite as all the integration tests but this requires switching off transpile-only, which would make running the tests slower. So I thought I'd wrap TSON.assertType in a generic and try/catch/ignore if error matches transform is not available, and disable transpileOnly only in the CI.

I tried commenting out the if statements protecting against generics but the end result was Error: Error on TSON.assertType(): invalid type on $input, expect to be unknown

KristjanTammekivi avatar Sep 11 '22 15:09 KristjanTammekivi

Have not understood how to implement it yet.

If someone knows how to, please send a PR.

samchon avatar Sep 12 '22 12:09 samchon

Hi, typia is amazing! I would also love to have support for generic types.

cedrikaagaard avatar Mar 07 '23 10:03 cedrikaagaard

This is out of my ability. Hope PR

samchon avatar Mar 07 '23 11:03 samchon

+1 for this feature.

In our case, we want to provide a standard wrapper for internal projects for request payload validation using Express, which would look something like this:

import { Response } from "express";
import { IValidation, validate as typia_validate } from "typia";

export const validate = <T>(req: Request): T => {
  const validation: IValidation<T> = typia_validate<T>(req.body);
  if (!validation.success) {
    throw new MyCustomValidationError((validation as IValidation.IFailure).errors);
  }
  return (validation as IValidation.ISuccess<T>).data;
};

So here it just breaks because of the additional level of abstraction.

As I see it, we would need another transformer here that generates specific-type functions for the generically-typed one before the transformer of typia kicks in.

This could be an arbitrary-length chain of transformers or loop over the source code until all generically-typed function that call one of the functions exposed by typia are resolved.

Am I on the right track here? Anyone can provide some insights into transformers and what's possible and what isn't?

enote-kane avatar Mar 20 '23 13:03 enote-kane

@enote-kane About generic function, I can't even sure whether it would be possible or not.

In your case, I think using currying function would be the best choice:

import typia from "typia";
import { Response } from "express";

export const validate = 
    <T>(checker: (input: T) => typia.IValidation<T>) => 
    (req: Request) => {
        const result: typia.IValidation<T> = checker(req.body);
        if (!result.success)
            throw new MyCustomValidationError(result.errors);
        return result.data;
};

validate(typia.createValidate<MyDto>())(req);

samchon avatar Mar 20 '23 17:03 samchon

@samchon Thanx for the hint, that works for me for the moment.

enote-kane avatar Mar 21 '23 08:03 enote-kane

I know this is a closed issue but I'm calling you here because I think it's dealing with the same topic. First of all, is this an issue that has proved impossible? If it is impossible, I think I should ask a question about the code below. I can't understand the two errors that occur in the two methods below.

type SomeGenericType<T extends string> = T extends '' ? never : string;
class TestClass<T extends string = '', P = SomeGenericType<T>> {
    /**
     * Error: Error on typia.random(): non-specified generic argument.
     */
    predict1() {
        return typia.random<P>(); // complie error
    }

    /**
     * non-compile error, but if I call this `predict` method, it occur error.
     *
     * for example, `TypeError: $pick(...) is not a function`
     */
    predict2() {
        return typia.random<SomeGenericType<T>>();
    }
}

new TestClass<'testStringType'>().predict2(); // error.

The annotations above each method describe the error. predict1 has an error at the time of compilation. I understand that this is because, like the error message, it is a generic type that cannot be specified at the time of compilation. The second predict2 would actually be the same type as P. However, there is an error at runtime after compiling. I have a question that the error predict2 is a runtime error unlike predict1. Is this a solvable problem?

kakasoo avatar May 23 '23 14:05 kakasoo

@kakasoo Not possible at now because TypeScript compiler API can't specify it. But don't know how it would be in future.

samchon avatar May 23 '23 15:05 samchon

Thank you for your quick response. So what do you think about the above two methods, one getting an error at the time of compilation, and the other getting an error at the time of runtime? I think it's safer for both to have errors at the time of compilation, but is there no solution to this?

kakasoo avatar May 23 '23 15:05 kakasoo

No plan to do it, due to it would take a lot of time, but low expected effect.

However, it is okay to challenge by yourself.

samchon avatar May 23 '23 15:05 samchon

I think this is a serious issue as it forces us to create a validation (or protobuf) function for every single DTO. In other words, it makes typia a non-scalable library.

lewispham avatar Oct 13 '23 14:10 lewispham

@lewispham Then make something like nestia, no way to accomplish this suggestion

samchon avatar Oct 13 '23 15:10 samchon

@samchon How does nestia handle this then? If I'm not mistaken then it looks like typia does not support creating validation function with reflective types.

lewispham avatar Oct 13 '23 16:10 lewispham

When you want to get only type info, guide users to use typia.json.application() function.

Otherwise you want to use TypeScript compiler API directly, do something like this'

  • https://github.com/samchon/nestia/blob/master/packages/core/src/programmers/TypedQueryRouteProgrammer.ts

samchon avatar Oct 16 '23 04:10 samchon