elysiajs / elysia

Ergonomic Framework for Humans
https://elysiajs.com
MIT License
9.09k stars 193 forks source link

Typing error when using `onError` with `derive` #653

Open nxht opened 1 month ago

nxht commented 1 month ago

What version of Elysia.JS is running?

1.0.21

What platform is your computer?

Linux 5.15.146.1-microsoft-standard-WSL2 x86_64 x86_64

What steps can reproduce the bug?

import { Elysia } from 'elysia';

const plugin = new Elysia({ name: 'test' }).derive({ as: 'global' }, () => {
  return {
    test: 'test',
  };
});

const app = new Elysia()
  .use(plugin)
  .onError(({ test }) => {
    console.log(test);
  })
  .get('/', ({ test }) => {
    throw new Error(test);
  })
  .listen(7003);

console.log(`http://${app.server?.hostname}:${app.server?.port}`);

What is the expected behavior?

No type error as the code works well. Type of test should be inferred as string | undefined or at least string. Also, there was no type error when tested on elysia@1.0.20

What do you see instead?

image

Additional information

Same code on elysia@1.0.20

image

RappyTV commented 1 month ago

My code looks like this:

function fetchI18n(app: Elysia) {
    return app.derive({ as: 'global' }, ({ headers }) => {
        const header = headers[`x-minecraft-language`] || `en_us`;
        const locales = getLocales(header);
        return {
            i18n: (path: string) => getPath(path, locales)
        };
    });
}

const elysia = new Elysia()
.use(fetchI18n)
.onError(({ code, set, error, i18n }) => {
    if(code == 'VALIDATION') {
        set.status = 422;
        return { error: i18n(error.message) };
    } else if(code == 'NOT_FOUND') {
        set.status = 404;
        return { error: i18n(`error.notFound`) };
    } else {
        set.status = 500;
        Logger.error(error.message);
        return { error: i18n(`error.unknownError`) };
    }
})
.listen(config.port);

I don't get any type errors and typescript also says that the function i18n in Elysia#onError exists but as soon as I run into an error (requesting a non-existent route for example) I get this error:

122 |     if(code == 'VALIDATION') {
123 |         set.status = 422;
124 |         return { error: i18n(error.message) };
125 |     } else if(code == 'NOT_FOUND') {
126 |         set.status = 404;
127 |         return { error: i18n(`error.notFound`) };
                              ^
TypeError: i18n is not a function. (In 'i18n("error.notFound")', 'i18n' is undefined)
      at /.../src/index.ts:127:25
GET - /test failed
nxht commented 1 month ago

My code looks like this:

function fetchI18n(app: Elysia) {
    return app.derive({ as: 'global' }, ({ headers }) => {
        const header = headers[`x-minecraft-language`] || `en_us`;
        const locales = getLocales(header);
        return {
            i18n: (path: string) => getPath(path, locales)
        };
    });
}

const elysia = new Elysia()
.use(fetchI18n)
.onError(({ code, set, error, i18n }) => {
    if(code == 'VALIDATION') {
        set.status = 422;
        return { error: i18n(error.message) };
    } else if(code == 'NOT_FOUND') {
        set.status = 404;
        return { error: i18n(`error.notFound`) };
    } else {
        set.status = 500;
        Logger.error(error.message);
        return { error: i18n(`error.unknownError`) };
    }
})
.listen(config.port);

I don't get any type errors and typescript also says that the function i18n in Elysia#onError exists but as soon as I run into an error (requesting a non-existent route for example) I get this error:

122 |     if(code == 'VALIDATION') {
123 |         set.status = 422;
124 |         return { error: i18n(error.message) };
125 |     } else if(code == 'NOT_FOUND') {
126 |         set.status = 404;
127 |         return { error: i18n(`error.notFound`) };
                              ^
TypeError: i18n is not a function. (In 'i18n("error.notFound")', 'i18n' is undefined)
      at /.../src/index.ts:127:25
GET - /test failed

@RappyTV For your case, Based on https://elysiajs.com/life-cycle/overview.html, Finding route is done before derive/transform lifecycle, so i18n actually can be undefined, in case of 'NOT_FOUND' error. So the correct typing would be Function | undefined and you should handle the undefined case

RappyTV commented 1 month ago

@RappyTV

For your case,

Based on https://elysiajs.com/life-cycle/overview.html,

Finding route is done before derive/transform lifecycle, so i18n actually can be undefined, in case of 'NOT_FOUND' error.

So the correct typing would be Function | undefined and you should handle the undefined case

@nxht The docs are pretty confusing then as they say that errors are at the last (eighth) position in the lifecycle