Ts-node: Can't extend express Request type

Created on 13 Dec 2018  路  23Comments  路  Source: TypeStrong/ts-node

I've tried this, this, etc

// ./typings/express/index.d.ts
declare namespace Express {
  export interface Request {
     token?: string
  }
}

Example usage:

import * as express from 'express'

(req: express.Request, res: express.Response, next: express.NextFunction) => {

  const foo = req.token

}

It does work if I compile directly (tsc -p .), it does work in Visual Code, but when I try to run with ts-node I always get:

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

Any idea how can I make it work with ts-node?

Versions: [email protected] [email protected]

https://stackoverflow.com/q/53765895/340760

Most helpful comment

To help anyone who is just looking for something else to try here is what worked for me when trying to extend ExpressJS' Request. I had to have tried more than a dozen things before getting this to work:

  • Flip the order of what everyone is recommending in the "typeRoots" of your tsconfig.json (and don't forget to drop the src pathing if you have a rootDir setting in tsconfig such as "./src"). Example:
"typeRoots": [
      "./node_modules/@types",
      "./your-custom-types-dir"
]
  • Example of custom extension ('./your-custom-types-dir/express/index.d.ts"). I had to use inline import and default exports to use classes as a type in my experience so that is shown too:
declare global {
  namespace Express {
    interface Request {
      customBasicProperty: string,
      customClassProperty: import("../path/to/CustomClass").default;
    }
  }
}
  • Update your nodemon.json file to add the "--files" command to ts-node, example:
{
  "restartable": "rs",
  "ignore": [".git", "node_modules/**/node_modules"],
  "verbose": true,
  "exec": "ts-node --files",
  "watch": ["src/"],
  "env": {
    "NODE_ENV": "development"
  },
  "ext": "js,json,ts"
}

All 23 comments

What鈥檚 your tsconfig.json?

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "noImplicitAny": false,
    "sourceMap": true,
    "outDir": "dist",
    "experimentalDecorators": true,
    "esModuleInterop": true
  },
  "exclude": ["node_modules"]
}

I also tried with "typeRoots": ["./node_modules/@types", "./typings"]

@brunolm That wouldn't work because it'd just discover the first one, not all of them. You can always use the /// <reference /> directive to resolve the one you specifically want to extend (and put your typeRoots first, otherwise you're just resolving @types anyway - it may even work by just switching the order of typeRoots above and no need for reference since one is a module and the other definition is global).

Oh, also, you probably should just put your overrides in a different folder and it would be a non-issue. The problem is the conflict between two Express packages being resolved, but you can create a different package named something to do with your app or anything else that doesn鈥檛 conflict.

As this is confusing a lot of people to get right, I'd suggest setting up a working example in the docs

I don't fully understand why that's required now. I can confirm that this worked before:

versions

"typescript": "2.3.4",
"ts-node": "3.0.4",

tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "noImplicitAny": false,
    "sourceMap": true
  }
}

typings/foo.d.ts

declare module Express {
  export interface Request {
    knex: any;
    config: any;
    client: { id: number; name: string; };
  }
}

Please see the README and CHANGELOG, and possibly search past issues to find why it was changed.

There is also an example in the README for adding custom types and modules. If you鈥檇 like to expand upon it, please feel free to submit a PR!

Just checked and we should invert the resolution order of types so it works as expected here.

Edit: The reason for the change is also in the README.

With --files in ts-node command local declaration files are recognized. Example command: ts-node --files src/index.ts

Can you guys @3mard @blakeembrey tell WHY this works? I would like understand more :) Also is this the best solution for this problem or is it only a workaround?

I gave up and started to do this:

req['token'] = 'foo'

And then casting to something when needed.

@henrikra I think this is a design decision that was made to optimize ts-node (why include .d.ts files that your app are not using anyway ?)

https://github.com/TypeStrong/ts-node/blob/master/src/index.ts#L481

@brunolm @henrikra I created this repo example https://github.com/3mard/ts-node-example for extending express request

Thanks to this comment by @blakeembrey, I got my declaration merges working! All you need to do is put "./typings" before "./node_modules/@types".

"typeRoots": ["./typings", "./node_modules/@types"]

@saoudrizwan Thanks it works :)

This worked for me:

  • create express.d.ts file in typings folder with contents
declare module 'express-serve-static-core' {
  interface Request {
    foo?: string
  }
}

@marcosfede your code makes everything be an any

image

If I delete your file the types work again.

Thanks to this comment by @blakeembrey, I got my declaration merges working! All you need to do is put "./typings" before "./node_modules/@types".

"typeRoots": ["./typings", "./node_modules/@types"]

Still not working
Screen Shot 2020-01-17 at 23 24 41

I solved this issues like this
(req as any).something = object;
it works for me

Thanks to this comment by @blakeembrey, I got my declaration merges working! All you need to do is put "./typings" before "./node_modules/@types".

"typeRoots": ["./typings", "./node_modules/@types"]

Still not working
Screen Shot 2020-01-17 at 23 24 41

I solved this issues like this
(req as any).something = object;
it works for me

Same. I can't make it work. Neither solution helps. Yours is an ugly hack but works for sure.

My problem is that VSCode recognizes it, and all is green. But when I try to run it with ts-node, it gives the error.

The following worked for me:

// project-root/src/types/express/index.d.ts
declare global {
    namespace Express {
        interface Request {
            token?: string
        }
    }
}

And then run

ts-node --files ./src/server.ts

As ts-node does not use files because of slows startup time, we need to tell it explicitly. Hence '--_files_' is necessary here.

VSCode gives me:

Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.ts(2669)

You need to import or export something to indicate that the file is a module.
If you are not importing anything then simply add

export {};

in that file, then the error should be gone.

To help anyone who is just looking for something else to try here is what worked for me when trying to extend ExpressJS' Request. I had to have tried more than a dozen things before getting this to work:

  • Flip the order of what everyone is recommending in the "typeRoots" of your tsconfig.json (and don't forget to drop the src pathing if you have a rootDir setting in tsconfig such as "./src"). Example:
"typeRoots": [
      "./node_modules/@types",
      "./your-custom-types-dir"
]
  • Example of custom extension ('./your-custom-types-dir/express/index.d.ts"). I had to use inline import and default exports to use classes as a type in my experience so that is shown too:
declare global {
  namespace Express {
    interface Request {
      customBasicProperty: string,
      customClassProperty: import("../path/to/CustomClass").default;
    }
  }
}
  • Update your nodemon.json file to add the "--files" command to ts-node, example:
{
  "restartable": "rs",
  "ignore": [".git", "node_modules/**/node_modules"],
  "verbose": true,
  "exec": "ts-node --files",
  "watch": ["src/"],
  "env": {
    "NODE_ENV": "development"
  },
  "ext": "js,json,ts"
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

max-block picture max-block  路  4Comments

nehalist picture nehalist  路  3Comments

aj-r picture aj-r  路  3Comments

dakom picture dakom  路  3Comments

OliverJAsh picture OliverJAsh  路  3Comments