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

Improve router.resource type-checking #81

Open
webNeat opened this issue Jan 27, 2024 · 14 comments
Open

Improve router.resource type-checking #81

webNeat opened this issue Jan 27, 2024 · 14 comments
Assignees
Labels
Type: Enhancement Improving an existing feature

Comments

@webNeat
Copy link

webNeat commented Jan 27, 2024

Package version

7.0.2

Describe the bug

Hello,

I noticed that router.resource does not give Typescript error when the given controller does not implement all the needed REST methods.

For example, the following code does not show any error but will fail at runtime:

import router from '@adonisjs/core/services/router'

class TestController {}

router.resource('test', TestController) // <- TS does not complain

The equivalent code using get, post, ... methods does show Typescript errors

import router from '@adonisjs/core/services/router'

class TestController {}

router.get('/test/create', [TestController, 'create'])  // <- TS complains
router.get('/test', [TestController, 'index'])  // <- TS complains
router.post('/test', [TestController, 'store'])  // <- TS complains
router.get('/test/:id', [TestController, 'show'])  // <- TS complains
router.get('/test/:id/edit', [TestController, 'edit'])  // <- TS complains
router.put('/test/:id', [TestController, 'update'])  // <- TS complains
router.patch('/test/:id', [TestController, 'update'])  // <- TS complains
router.delete('/test/:id', [TestController, 'destroy'])  // <- TS complains

A simple test for this would be:

test.group('Router | typechecking', () => {
  test('router.resource gives Typescript error when a RESTfull method is missing in the given controller', () => {
    class TestControllerClass {}
    const router = new RouterFactory().create()
    router.resource('test', TestControllerClass) // @ts-expect-error
  })
})

Can I make a PR to fix this?

Reproduction repo

No response

@RomainLanz
Copy link
Member

Hey @webNeat! 👋🏻

You need to ensure your type will work for any modifier like only(), except() and apiOnly().

@webNeat
Copy link
Author

webNeat commented Jan 27, 2024

Thanks for the remark @RomainLanz, I missed those.

I see the difficulty now. I will give it a shot and let you know if I could solve it.

@Julien-R44
Copy link
Member

it won't be possible since only(), except() and apiOnly() are called AFTER using resource() method

this means that you won't be able to accumulate type information in a generic. because when resource is called, the type system has no way of knowing that apiOnly will be called next

Or maybe there is a typescript wizardy that i am not aware of !

@webNeat
Copy link
Author

webNeat commented Jan 29, 2024

You are right @Julien-R44, I didn't find any way to do it using only normal types. So I made it possible using a Typescript plugin:

adonis-ts-plugin.mp4

This plugin is still a POC (missing some edge cases, tests, ...), There might be other typechecking improvements that can be added to the Adonis framework using this approach.

My question now is whether this plugin should be part of @adonisjs/tsconfig to be included by default on all projects, or should it be a third-party library? Either way, I am ready to work on it :)

Let me know what you think.

@Julien-R44
Copy link
Member

that looks cool dude, well done. making a typecript plugin really doesn't look easy ahah

my two cents on this: with V6, we've finally been able to get rid of our custom compiler. we are now only using standard stuff that is supported everywhere

i think this is the right way to go. Using a custom ts plugin for typechecking seems a bit twisted to me, and if we want to have stricter typechecking on the router, then we should instead redesign the router API, rather than developing a non-standard typechecking plugin and having to maintain it

@thetutlage
Copy link
Member

@webNeat That looks impressive.

First, I want to understand how TypeScript plugins are applied and are they picked by all the editors using TypeScript LSP? If yes, then I might be tempted to use it.

Because, @Julien-R44 if we change router API, then we will introduce a breaking change, something I would like to avoid.

So yeah let's explore and see what is best in this case and keep all options open.

@thetutlage thetutlage added the Type: Enhancement Improving an existing feature label Jan 30, 2024
@Julien-R44
Copy link
Member

after some research, I noticed that Next.JS also offers a typescript plugin:

Doc : https://nextjs.org/docs/app/building-your-application/configuring/typescript#typescript-plugin

Source : https://github.com/vercel/next.js/blob/b8a7efcf1361da29994247f7d2dd6b58912b6a9e/packages/next/src/server/typescript/index.ts

In fact, im afraid that it's looks too "magical", or that it will cause errors that are hard to understand because nobody's ever seen them, because specific to our plugin

but my fear probably comes from the fact that i'm not familiar with typescript plugins and have never used one

@thetutlage
Copy link
Member

@webNeat How about releasing this plugin. Let us use it for a while and provide you the feedback. If everything feels smooth, we can go ahead and add it to the default config.

@webNeat
Copy link
Author

webNeat commented Jan 31, 2024

@thetutlage That works for me. The alpha version I used in the demo is already on npm https://www.npmjs.com/package/adonis-ts-plugin

Here is how to use it:

  1. install it npm i -D adonis-ts-plugin
  2. add it to tsconfig.json
{
  "compilerOptions": {
    // ...
    "plugins": [{ "name": "adonis-ts-plugin" }]
  },
}
  1. Configure VSCode to use the workspace Typescript instead of the global one (as explained in the Nextjs docs):
  • Open the command palette (Ctrl/⌘ + Shift + P)
  • Search for "TypeScript: Select TypeScript Version"
  • Select "Use Workspace Version"

I will try to test other editors and see how it works with them.

Here is the list of known bugs/limitations, in this alpha version, I am woking on:

  1. it considers all controller methods (it should only consider public methods that match the prototype (ctx: HttpContext, ...args) => any).
  2. does not consider inherited methods.
  3. requires the argument of only and except to be an array literal, so the following does not work yet:
const ignoredRoutes = ['edit', 'destroy']
const storeRoute = 'store'

router.resource('demo', DemoController)
  .only([storeRoute, 'destroy']) // <- doesn't know that `storeRoute` is `'store'`

router.resource('demo', DemoController)
  .except(ignoredRoutes) // <- doesn't resolve the value of `ignoredRoutes`

Let me know if you found any other bugs.

@thetutlage
Copy link
Member

Cool. So yeah, it seems like there are some usability issues right now. But, I will still give it a try personally and report other issues (if I find any)

@webNeat
Copy link
Author

webNeat commented Mar 14, 2024

Hello @thetutlage,

it took me some time but I finally fixed all the issues above and released the version 1.0.0 of the plugin.

https://github.com/webNeat/adonis-ts-plugin

Feel free to try it and let me know if you find any issues.

@thetutlage
Copy link
Member

Hey @webNeat

Sorry, it took unexpected long to get back to you. Just got busy with too many things.

I will give the plugin a try this week and share my feedback here.

Thanks a ton for taking out time and working on it :)

@webNeat
Copy link
Author

webNeat commented Apr 22, 2024

Hello @thetutlage

No worries, take your time in testing it and let me know what you find :)

@ThisIsMissEm

This comment was marked as outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Enhancement Improving an existing feature
Projects
None yet
Development

No branches or pull requests

5 participants