53
votes

I have the following piece of code in my express app

router.get('/auth/userInfo', this.validateUser,  (req, res) => {
    res.json(req.user);
});

and my IDE seems to be complaining with the error

error TS2339: Property 'user' does not exist on type 'Request'.

When I compile my typescript code it seems to be throwing this error. Any ideas why this is happening?

11
probably because of missing type on req - fast fix (req: any, res: any) => ... otherwise you might need to use some type info for expressOvidiu Dolha

11 Answers

65
votes

We have a large API written in Express and Typescript, and this is how we handle such scenarios:

We keep the request definitions in one file:

import { Request } from "express"
export interface IGetUserAuthInfoRequest extends Request {
  user: string // or any other type
}

And then in the file where we are writing the controller functions:

import { Response } from "express"
import { IGetUserAuthInfoRequest } from "./definitionfile"

app.get('/auth/userInfo', validateUser,  (req: IGetUserAuthInfoRequest, res: Response) => {
  res.status(200).json(req.user); // Start calling status function to be compliant with Express 5.0
});

Be advised that "user" is not a property that is available natively in the Request object of Express. Make sure that you are using a middleware that adds such property to the request object.

28
votes

req is probably of type Request from "express" package and user does not exist there. You have to either extend Request with own router handler or cast it to type any or object.

try res.json(req['user']) or res.json( (<any>req).user )

You may also use module/global augmentation

import { Request } from "express"

declare module "express" { 
  export interface Request {
    user: any
  }
}

You can also make your own handler wrapper (instead of extending Router functionality in ExpressJs).

import * as express from 'express';

interface IUserRequest extends express.Request {
    user: any
}

function myHandler(handler: (req: IUserRequest, res: express.Response, next?: express.NextFunction) => any) {
    return (req: express.Request, res: express.Response, next: express.NextFunction) => {
        try {

            validateUser(req, res, (err) => { // your validateUser handler that makes a user property in express Request
                if(err)
                     throw err;

                let requestWrapper: IUserRequest = <IUserRequest>req;

                handler(requestWrapper, res, next);
            })                
        }
        catch (ex) {
            next(ex);
        }
    } 
}

let app = express();
// init stuff for express but this.validateUser handler is not needed

app.use('/testuser', myHandler((req, res) => {
    res.json(req.user);
}));
13
votes

You're getting this error because there's no type definition for the user property in the the native express Request object. You should install the type definitions for the middleware you're using to add user to the request.

For example, if you're using the passport library for JWT authentication as middleware:

router.get('/auth/userInfo', passport.authenticate('jwt', {session:false}), (req, res, next) => {
  // Get their info from the DB and return it
  User.findOne({ email: req.user.email }, (err, user) => {
    if (err) {return next(err);}
    ...
    ...

You should add the type definitions for passport:

npm install --save @types/passport
11
votes

You need to make a Declaration Merging:

"Declaration merging means that the compiler merges two separate declarations declared with the same name into a single definition."

To do that you can creat a file called type.d.ts at your project src folder (or wherever you want) with the following content:

declare namespace Express {
  export interface Request {
      user: any;
  }
  export interface Response {
      user: any;
  }
}

Here we are telling the compiler to add user propertie to our Request and Response definiton.

Next, we need to attach this to our tsconfig.json.

Example:

{
  "compilerOptions": {
      "module": "commonjs",
      "moduleResolution": "node",
      "pretty": true,
      "sourceMap": true,
      "target": "es6",
      "outDir": "./dist",
      "baseUrl": "./lib"
  },
  "include": [
      "lib/**/*.ts"
  ],
  "exclude": [
      "node_modules"
  ],
  "files":["types.d.ts"]
}

Now, the typescript compiler know that Request, has a propertie called user that in my case can accept any json object. You can restrict the type for string if you want.

The final result is that, no error in VSCode =)

2
votes

For people using GraphQL, there comes a problem with the graphqlExpress function not accepting your new interface. I was attempting to follow this guide until I ran into this exact problem. I have figured out this solution:

this.app.use("/graphql", bodyParser.json(), this.auth, graphqlExpress((req: AuthRequest | undefined) => ({ 
    schema,
    context: {
        user: req!.user
    }
})));

And in the AuthRequest interface:

import {Request} from "express"

export interface AuthRequest extends Request {
    user?: string
}

Typescript will not allow you to use AuthRequest unless it is also allowed to be undefined. Therefore the | undefined. (source)

After this point, the user object does not 100% exist on the Request object, hence the ? after user.

2
votes

If you're using ts-node and not ts-node-dev, do this:

  1. Create a typings folder in your src folder.
  2. Create a folder within the typings folder with the name of the package you intend to extend.
  3. Create an index.d.ts file with the new folder.

In my case, I am extending express, I ended up with something like this:

  src/
    - typings/
      - express/
        - index.d.ts

within the index.d.ts file I have this:

declare module Express {
    export interface Request {
        bucketUrl: string;
        fileName: string;
    }
}

Remember to update your .tsconfig:

{
  "compilerOptions": {
    "typeRoots" : ["./node_modules/@types", "./typings"]
  }
}
1
votes

I was using okta-angular-node with express for this https://developer.okta.com/blog/2018/10/30/basic-crud-angular-and-node

I came up with similar error for req.user. Please check the server/auth.ts inside the above given link.

Type casting worked for me. Try to cast as follows

req.user to (req['user'])

0
votes

Old question but if anyone stumbles across this like I did, take a look at 'Declaration Merging' - solved the issue for me.

https://www.typescriptlang.org/docs/handbook/declaration-merging.html

https://truetocode.com/extend-express-request-and-response-typescript-declaration-merging/

0
votes

Add a typeRoots to your tsconfig.json, this will tell typescript where to look to find declaration files. By default typescript looks in /node_modules/@types, but when you specify this property, those defaults are cleared. You can read more here.

tsconfig.json

{
  "compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./src/util"]
  }
}            

types.d.ts

import { User } from '../models/user'

declare global {
  namespace Express {
    interface Request {
      user: User
    }
  }
}

Folder structure

node_modules
tsconfig.json
/src/
   /models/
      user.ts
   /util/
      types.d.ts
0
votes

You need to decorate the request using fastify decorators as mentioned below,

fastify.decorateRequest('user', <pass null or empty string here>)

and handle what should be in the user object.

Official document - https://www.fastify.io/docs/latest/Decorators/

0
votes

You can try including the below snippet in your middleware.ts file:

declare module 'express-serve-static-core' {
  interface Request {
    user?: string
  } 
}