Express + TypeScript: Create type inference for response.locals
Asked Answered
D

4

9

I want to add types for my response.locals. It is used to append data to your request-response cycle.

What I tried

// ./types/express/index.d.ts
declare global {
    declare namespace Express {
        interface Response {
            locals: {
                userId: number;
            };
        }
    }
}

// tsconfig.json
    "compilerOptions": {
        "typeRoots": ["./types"],
    }
myController.post("/", async (request, response) => {
     // Can't get type in my controller
    const { userId } = response.locals; // <- userId is any

Goal: Get correct type inference for my response.locals variable

Versions: "express": "^4.17.1", "@types/express": "^4.17.8", "typescript": "^4.5.4"

Dialytic answered 14/1, 2022 at 15:26 Comment(7)
Does this help you? https://mcmap.net/q/1315530/-how-to-specify-type-returned-with-express-responseTurgeon
@Turgeon no, not really :/. I'm looking for a way to extend response.locals through an additional declaration file.Dialytic
typeRoots option has a default value; make sure to include itColene
Have you tried including it in the files section of tsconfig?Tightlipped
@Tightlipped Yes, I tried to include it there. Didn't change anything, unfortunetaly.Dialytic
@DimaParzhitsky How do you include the default option? I couldn't find the answer in the link you provided.Dialytic
@Sirhennihau Actually, my bad, this is harder than I thought it would beColene
V
9

Looking at @types/express-serve-static-core/index.d.ts we can see that there is a global namespace Express with some interfaces that we can extend. One of them is Locals :)

The solution:

src/types.d.ts (or similar, include it in tsconfig, eg in the "types" field)

declare global {
  namespace Express {
    interface Locals {
      anything: 'here'
    }
  }
}
Valleau answered 23/2, 2023 at 15:17 Comment(1)
What did it for me was declare module 'express-serve-static-core' instead of namespace ExpressUrbanite
P
2

Without a proper TypeScript background, I was able to compile the following (after some trial and error):

import {Request, Response} from "express";
interface Locals extends Record<string, any> {
  userId: number;
}
interface MyResponse extends Response {
  locals: Locals;
}
export function middleware(request: Request, response: MyResponse) {
  const {userId} = response.locals;
  response.end(userId);
  return userId;
}

The declaration file (.d.ts) contains:

export declare function middleware(request: Request, response: MyResponse): number;

so the number type was correctly inferred.

And it works when I use this middleware function in express.

Peeples answered 20/1, 2022 at 15:49 Comment(2)
userId is still assumed as any by TypeScript. Yes, it compiles. But I don't achieve type-safety this way.Dialytic
I have edited my answer.Yokel
A
0

You can checkout my new lib express-typed which is small type safe typescript wrapper around express router.

With express-typed the returned types are inferred and exported from your backend, and later imported from your frontend.

Successfully deployed to production and improved development cycles!

Annulation answered 6/5 at 19:9 Comment(0)
G
0

This worked for me:

// ./types/express/index.d.ts
declare namespace Express {
  interface Locals {
    userId: number;
  }
}

add the ./types/express/index.d.ts inside "files" and "include" field in tsconfig.json

// tsconfig.json
{
  "compilerOptions": {
    ...
  },
  "include": ["./**/*.ts"],
  "files": ["./types/express/index.d.ts"]
}

*field "files" will not accept glob patterns, must add full path

Gipsy answered 10/8 at 18:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.