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

Typecheck breaks for response schema not containing 200 status code #785

Open
aburii opened this issue Aug 19, 2024 · 8 comments
Open

Typecheck breaks for response schema not containing 200 status code #785

aburii opened this issue Aug 19, 2024 · 8 comments
Labels
bug Something isn't working

Comments

@aburii
Copy link

aburii commented Aug 19, 2024

What version of Elysia is running?

latest

What platform is your computer?

Darwin 23.5.0 arm64 arm

What steps can reproduce the bug?

import { Elysia, t } from "elysia";

const app = new Elysia()
    .get("/hello", () => "Hello Elysia", {
        response: {
            200: t.String()
        }
    })
    .get('/wrong', () => {
        return { 201: 1 } // Should be marked as error  as below
    }, {
        response: {
            201: t.String()
        }
    })
    .get('/wrong-200', () => {
        return { 200: 1 } // Raises the error
    }, {
        response: {
            200: t.String()
        }
    })
    .listen(3000);

console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

Here is the screenshot of my IDE (WebStorm 2024.2)

image

What is the expected behavior?

The expected behavior is that all 20(X) (201, 204 and so on) typecheck work.

In this case

    .get('/wrong', () => {
        return { 201: 1 }
    }, {
        response: {
            201: t.String()
        }
    })

Should raise the same error as for 200 status code like:

image

Details:

image

What do you see instead?

201 response schema does not trigger typecheck error raise.

Additional information

I am 100% this error does NOT come from my LSP nor my IDE since I have cleaned up the cache and restarted the services multiples times.

It seems that the pasts fixes have been removed (e.g. PR #596 )

Have you try removing the node_modules and bun.lockb and try again yet?

Yes

@aburii aburii added the bug Something isn't working label Aug 19, 2024
@bogeychan
Copy link
Contributor

bogeychan commented Aug 19, 2024

The type behaviour is correct.

201 is the status code, no response body value.

response: {
    201: t.String()
//  ^ status
//            ^ body value
}

Based on your example, read the comments:

new Elysia()
  .get(
    "/hello",
    () => "Hello Elysia", // no error because you return string with 200 status code
    {
      response: {
        200: t.String(),
      },
    }
  )
  .get(
    "/wrong",
    () => {
      return { 201: 1 }; // no error because you implicit return 200 status code with json body of { 201: 1 }
    },
    {
      response: {
        201: t.String(),
      },
    }
  )
  .get(
    "/wrong-200",
    () => {
      return { 200: 1 }; // This raises error because 200 response schema (see below) overwrites the default. and returning here should be string, not object (json)
    },
    {
      response: {
        200: t.String(),
      },
    }
  );

@bogeychan
Copy link
Contributor

Use this if you wanna make the last error work:

app.get(
  "/wrong-200",
  () => {
    return { 200: 1 };
  },
  {
    response: {
      200: t.Object({
        200: t.Number(),
      }),
    },
  }
);

@bogeychan
Copy link
Contributor

bogeychan commented Aug 19, 2024

All this kinda follows the OpenAPI responses semantic: https://swagger.io/docs/specification/describing-responses/

@aburii
Copy link
Author

aburii commented Aug 19, 2024

@bogeychan same issue here on this code

import { Elysia, t } from "elysia";

const app = new Elysia()
    .get("/hello", () => "Hello Elysia", {
        response: {
            200: t.String()
        }
    })
    .get('/wrong', ({ set }) => {
        set.status = 'Created'
        return 1 // Should be marked as error  as below
    }, {
        response: {
            201: t.String()
        }
    })
    .get('/wrong-200', () => {
        return 1 // Raises the error
    }, {
        response: {
            200: t.String()
        }
    })
    .listen(3000);

console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

The first examples I've shown are indeed not correct.

Here is the screenshot of the current error state in my IDE (WebStorm 2024.2)

image

I have to mention this issue has already been fixed by april but it seems to happen again, please refer to #596 and #490

@bogeychan
Copy link
Contributor

@aburii thanks for sharing this finding.


@SaltyAom

@aburii
Copy link
Author

aburii commented Aug 19, 2024

@aburii thanks for sharing this finding.

@SaltyAom

And please ignore the firsts examples I provided, they are incorrect, only the lasts are. By trying to illustrate more on the first examples I've shown no relevant issue.

What I tried to share are in fact the last examples.

@SaltyAom
Copy link
Member

@bogeychan same issue here on this code

import { Elysia, t } from "elysia";

const app = new Elysia()
    .get("/hello", () => "Hello Elysia", {
        response: {
            200: t.String()
        }
    })
    .get('/wrong', ({ set }) => {
        set.status = 'Created'
        return 1 // Should be marked as error  as below
    }, {
        response: {
            201: t.String()
        }
    })
    .get('/wrong-200', () => {
        return 1 // Raises the error
    }, {
        response: {
            200: t.String()
        }
    })
    .listen(3000);

console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);

The first examples I've shown are indeed not correct.

Here is the screenshot of the current error state in my IDE (WebStorm 2024.2)

image I have to mention this issue has already been fixed by april but it seems to happen again, please refer to #596 and #490

The /wrong here is not possible due to limitation of TypeScript. As it's not possible to infer the type of set.status = 201, Elysia assume the endpoint returning 200 status code.

To make this possible, Elysia has a dedicated error function to validate type integrity.

Screenshot 2567-08-27 at 15 28 31

So if we refactor the code to use error function as the following:

new Elysia()
    .get('/wrong', ({ error }) => {
        return error(201, 1)
    }, {
        response: {
            201: t.String()
        }
    })
    .listen(3000)

We can see that the IDE now show the error.
Screenshot 2567-08-27 at 15 28 54

So in other words, I recommended to use a dedicated error function instead of using set.status to ensure type integrity as using set.status isn't possible to infer return type due to TypeScript limitation.

@aburii
Copy link
Author

aburii commented Aug 28, 2024

@SaltyAom Feels a bit weird to call a function 'error' to send back a success status, is there any function planned on next versions of Elysia to feel more accurate ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants