denooak

Generic type RouterContext requires 3 parameters - problem


I'm trying to update the version of the modules in my deno project but after updating them I get the following errors and I don't understand why it happens. has anyone encountered this problem?

error:

* [ERROR]: Generic type 'RouterContext<R, P, S>' requires between 1 and 3 type arguments. export const Register = async ({request, response}: RouterContext) => { ~~~~~~~~~~~~~ at file:///Users/X/Documents/DenoAPP/src/controller.ts:11:53

*

controller.ts:

import {RouterContext} from "https://deno.land/x/oak/mod.ts";
import {Bson} from "https://deno.land/x/mongo@v0.29.2/mod.ts";
import * as bcrypt from "https://deno.land/x/bcrypt/mod.ts";
import {create, verify} from "https://deno.land/x/djwt@v2.4/mod.ts"

import {db} from "./database/connection.ts";
import UserSchema from './schemas/user.ts';

const users = db.collection<UserSchema>("users");

export const Register = async ({request, response}: RouterContext) => {
    const {name, email, password} = await request.body().value;

    const _id = await users.insertOne({
        name,
        email,
        password: await bcrypt.hash(password)
    })

    const user = await users.findOne({_id});

    delete user.password;

    response.body = user;
}

export const Login = async ({request, response, cookies}: RouterContext) => {
    const {email, password} = await request.body().value;

    const user = await users.findOne({email});

    if (!user) {
        response.body = 404;
        response.body = {
            message: 'User not found!'
        };
        return;
    }

    if (!await bcrypt.compare(password, user.password)) {
        response.body = 401;
        response.body = {
            message: 'Incorrect password!'
        };
        return;
    }

    const jwt = await create({alg: "HS512", typ: "JWT"}, {_id: user._id}, "secret");

    cookies.set('jwt', jwt, {httpOnly: true});

    response.body = {
        message: 'success'
    };
}

export const Me = async ({response, cookies}: RouterContext) => {
    const jwt = cookies.get("jwt") || '';

    if (!jwt) {
        response.body = 401;
        response.body = {
            message: 'unauthenticated'
        };
        return;
    }

    const payload = await verify(jwt, "secret", "HS512");

    if (!payload) {
        response.body = 401;
        response.body = {
            message: 'unauthenticated'
        };
        return;
    }

    const {password, ...userData} = await users.findOne({_id: new Bson.ObjectId(payload._id)});

    response.body = userData;
}

export const Logout = async ({response, cookies}: RouterContext) => {
    cookies.delete('jwt');

    response.body = {
        message: 'success'
    }
}


Solution

  • RouterContext is a generic interface, and you must provide at least the first type parameter. Because you are not supplying any, you receive the compiler error.

    This is the interface declaration:

    /** The context passed router middleware.  */
    export interface RouterContext<
      R extends string,
      P extends RouteParams<R> = RouteParams<R>,
      // deno-lint-ignore no-explicit-any
      S extends State = Record<string, any>,
    > extends Context<S> {
      /** When matching the route, an array of the capturing groups from the regular
       * expression. */
      captures: string[];
    
      /** The routes that were matched for this request. */
      matched?: Layer<R, P, S>[];
    
      /** Any parameters parsed from the route when matched. */
      params: P;
    
      /** A reference to the router instance. */
      router: Router;
    
      /** If the matched route has a `name`, the matched route name is provided
       * here. */
      routeName?: string;
    
      /** Overrides the matched path for future route middleware, when a
       * `routerPath` option is not defined on the `Router` options. */
      routerPath?: string;
    }
    

    Update in response to your comment and the offsite code that you shared:

    First, thanks for providing a reproducible example.

    After looking at your code, I see that there are other, unrelated type issues which I will not attempt to address in this answer. (However, you are always welcome to ask a new question.)

    These refactoring steps should help you solve the compiler error described in your question:

    First, it's good practice to manage your dependencies in a central location for each project (to avoid lengthy and duplicated import statements, and to reduce the likelihood of using different, incompatible versions of the same external dependencies). This can be achieved by re-exporting your external dependencies from a new module (./deps.ts) and then importing from that module in all other modules:

    ./deps.ts:

    export * as bcrypt from "https://deno.land/x/bcrypt@v0.3.0/mod.ts";
    
    export {
      Application,
      Router,
      // type RouterContext, // This is removed in favor of the next one
      type RouterMiddleware, // This one is new: I included it for a later step in this answer
    } from "https://deno.land/x/oak@v10.4.0/mod.ts";
    
    export { Bson, MongoClient } from "https://deno.land/x/mongo@v0.29.2/mod.ts";
    
    export { create, verify } from "https://deno.land/x/djwt@v2.4/mod.ts";
    
    export { oakCors } from "https://deno.land/x/cors@v1.2.2/mod.ts";
    
    

    Then, in other modules which need the external dependencies (for example, ./src/controller.ts):

    import { bcrypt, Bson, create, type RouterContext, verify } from "../deps.ts";
    
    // ...
    

    Now, to the compiler problem that you asked about:

    The solution which requires the least refactoring is to utilize a type annotation on your middleware function expressions rather than explicitly typing the parameters. In your module ./src/controllers.ts:

    import {
      bcrypt,
      Bson,
      create,
      type RouterMiddleware, // You'll need this type import
      verify,
    } from "../deps.ts";
    
    // Skipping other module code: just focusing on the middleware...
    
    // before
    export const Register = async ({ request, response }: RouterContext) => {/* ... */}; /*
                                                          ~~~~~~~~~~~~~
    Generic type 'RouterContext<R, P, S>' requires between 1 and 3 type arguments.deno-ts(2707) */
    
    // after
    export const Register: RouterMiddleware<string> = async (
      { request, response },
    ) => {/* ... */};
    
    // and make the same change for every other midddleware which currently uses the `RouterContext` type
    
    

    Afterward, you should stop seeing the compiler error that you described in your question.