2

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'
    }
}

Boris
  • 69
  • 1
  • 7

2 Answers2

2

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.

jsejcksn
  • 27,667
  • 4
  • 38
  • 62
  • the older version works without a problem .. I do not understand what parameter to add? give advice – Boris Mar 12 '22 at 11:38
  • @Boris Unfortunately, that's still not enough. You'll have to provide a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). (Ultimately, you'll need to use a [generic type parameter for inference](https://www.typescriptlang.org/docs/handbook/2/functions.html#inference), but I can't advise you unless I can see how you're using the middleware functions). – jsejcksn Mar 12 '22 at 17:15
  • I give a link to the full code https://codecollab.io/@proj/WeatherDatabaseSoup .. i tried "Inference" but it doesn't work again – Boris Mar 12 '22 at 18:10
0

Just pass the context to the RouterContext. Example -

Change this line: export const Login = async ({request, response, cookies}: RouterContext)

To export const Login = async ({request, response, cookies}: RouterContext<"/login">)

Basically, we need to tell RouterContext on what is the path for which it is trying to inject the dependency

  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jun 20 '22 at 02:06