17

I'm using Express and I'm trying to explicitly define res.locals. In the @types/express package, Express.Response.locals is any, so I can't seem to overwrite it:

types/express/index.d.ts:

declare namespace Express {
  interface Response {
    locals: {
      myVar: number
    }
  }
}

My Middleware:

import * as express from 'express'

function middleware(
  req: express.Request, 
  res: express.Response, 
  next: express.nextFunction
) {
  res.locals.myVar = '10' // I want this to throw a compiler error
  next()
}

I want my wrong assignment of res.locals.myVar to error, but res.locals is still any according to my autocompletion.

How can I remove any and completely replace it?

Daniel Diekmeier
  • 3,401
  • 18
  • 33

3 Answers3

17

I recently ran into this issue and managed to resolve it by creating an index.d.ts in my src folder to overwrite res.locals, my implementation looked like this:

// src/index.d.ts
import 'express';

interface Locals {
  message?: string;
}

declare module 'express' {
  export interface Response  {
    locals: Locals;
  }
}

Make sure you also have it included in your tsconfig.json, e.g

// somewhere in your tsconfig.json
  "include": [
    "src/**/*.ts"
  ]

You would use the interface just as you would normally

import { Request, Response, NextFunction } from 'express';

export const handler = (req: Request, res: Response, next: NextFunction) => {
  // should be typed
  res.locals.message = 'hello'
}

Hope this helps!

Mártin Alcalá
  • 4,589
  • 1
  • 7
  • 13
  • Hi Faris, thanks for the answer. I tried it, but with the provided solutions all other types of 'express' are gone. How did you resolve this? – natterstefan Feb 03 '20 at 10:36
  • 2
    Hey @natterstefan, make sure you import express at the top of the `index.d.ts` – Faris Tangastani Feb 03 '20 at 15:22
  • Hey @Faris, thanks for the quick response. Can you update and complete the example above, please? Even importing `express` does not resolve it. Maybe you can also add an example usage, eg. `const handler: Express.Handler = (req, res, next) => { ... }`. `res` should then have correct typings, right? Thank you! – natterstefan Feb 04 '20 at 09:39
  • Hey @natterstefan I added some extra stuff that might help. – Faris Tangastani Feb 05 '20 at 10:54
  • 1
    Thank you @Faris, worked for me now. Do you also know how to extend `Express.Handler ` so I do not need to add `res: Response`, but still be able to use the custom Response interface?. Edit: Maybe you can link to this resource as well in your answer -> https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express-serve-static-core/index.d.ts#L17-L25 – natterstefan Feb 11 '20 at 06:48
  • 1
    I think this answer is now obsolete. Here's what worked for me (express `4.16.1`): https://stackoverflow.com/a/55718334/3670829 – ghashi Jul 10 '20 at 04:55
  • 1
    Also didn't work for me in express 4.17 – Sir hennihau Jan 14 '22 at 15:36
  • 1
    Working solution https://stackoverflow.com/a/75546820/5043802 – elnygren Feb 23 '23 at 15:21
2

Unfortunately there is no way to override any using interface merging. You can so some surgery on the type and replace the type using mapped and conditional types:

import * as express from 'express'

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

type MyResponse = Omit<express.Response, "locals"> & { 
  locals: {
    myVar: number
  }
}
function middleware(
  req: express.Request, 
  res: MyResponse, 
  next: express.NextFunction
) {
  res.locals.myVar = '10' // error now
  next()
}
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • 1
    This throws `Type '(request: Request, response: MyResponse) => Promise | undefined>' is missing the following properties from type 'Application': init, defaultConfiguration, engine, set, and 61 more.` for me. I'm using express `4.17`. – Sir hennihau Jan 18 '22 at 11:44
2

The current accepted answer (https://stackoverflow.com/a/57509904/5043802) is outdated and broken. The new solution is to add the following to a *.d.ts file in your project (that is included by tsconfig):

// src/index.d.ts
declare global {
  namespace Express {
    interface Locals {
      shopify: { session: Session }
    }
  }
}

Explanation can be found from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b986359dc0a8a2108b777c3eb7d3d51909408631/types/express-serve-static-core/index.d.ts#L16

elnygren
  • 5,000
  • 4
  • 20
  • 31
  • this works really nicely – Adophilus May 05 '23 at 10:14
  • @elnygren This still lets you put anything on the interface w/o it caring. Do you know how to make it so locals is only what you specify? In this example only shopify we be on the locals and if you tried to write something else it would error out. – topched Aug 21 '23 at 23:24