web-dev-qa-db-fra.com

Étendre un objet Express Request à l'aide de Typescript

J'essaie d'ajouter une propriété pour exprimer un objet de requête à partir d'un middleware à l'aide de TypeScript. Cependant, je ne sais pas comment ajouter des propriétés supplémentaires à l’objet. Je préférerais ne pas utiliser la notation entre crochets, si possible.

Je cherche une solution qui me permettrait d’écrire quelque chose de similaire à ceci (si possible):

app.use((req, res, next) => {
    req.property = setProperty(); 
    next();
});
69
Isak Ågren

Vous souhaitez créer une définition personnalisée et utiliser une fonctionnalité dans TypeScript appelée Fusion de déclarations . Ceci est couramment utilisé, par exemple. dans method-override .

Créer un fichier custom.d.ts et assurez-vous de l'inclure dans votre tsconfig.json _ section files- le cas échéant. Le contenu peut ressembler à ceci:

declare namespace Express {
   export interface Request {
      tenant?: string
   }
}

Cela vous permettra, à tout moment dans votre code, d'utiliser quelque chose comme ceci:

router.use((req, res, next) => {
    req.tenant = 'tenant-X'
    next()
})

router.get('/whichTenant', (req, res) => {
    res.status(200).send('This is your tenant: '+req.tenant)
})
84
maximilianvp

Comme suggéré par le commentaires dans le index.d.ts , vous déclarez simplement à l’espace de nom global Express tout nouveau membre. Exemple:

declare global {
  namespace Express {
    interface Request {
      context: Context
    }
  }
}

Exemple complet:

import * as express from 'express';

export class Context {
  constructor(public someContextVariable) {
  }

  log(message: string) {
    console.log(this.someContextVariable, { message });
  }
}

declare global {
  namespace Express {
    interface Request {
      context: Context
    }
  }
}

const app = express();

app.use((req, res, next) => {
  req.context = new Context(req.url);
  next();
});

app.use((req, res, next) => {
  req.context.log('about to return')
  res.send('hello world world');
});

app.listen(3000, () => console.log('Example app listening on port 3000!'))

L'extension des espaces de noms globaux est couverte plus sur mon GitBook .

47
basarat

La réponse acceptée (comme les autres) ne fonctionne pas pour moi mais

declare module 'express' {
    interface Request {
        myProperty: string;
    }
}

fait. J'espère que cela aidera quelqu'un.

17
max-lt

Aucune des solutions proposées n'a fonctionné pour moi. J'ai fini par simplement étendre l'interface de requête:

import {Request} from 'express';

export interface RequestCustom extends Request
{
    property: string;
}

Puis l'utiliser:

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

someMiddleware(req: RequestCustom, res: Response, next: NextFunction): void
{
    req.property = '';
}

Edit: Des versions récentes de TypeScript s'en plaignent. Au lieu de cela, je devais faire:

someMiddleware(expressRequest: Request, res: Response, next: NextFunction): void
{
    const req = expressRequest as RequestCustom;
    req.property = '';
}
9
Tom Mettam

Pour les nouvelles versions d’express, vous devez augmenter le express-serve-static-core module.

Cela est nécessaire car l'objet Express provient désormais de là: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8fb0e959c2c7529b5fa4793a44b41b797ae671b9/types/express/index.d.ts#L19

Fondamentalement, utilisez ce qui suit:

declare module 'express-serve-static-core' {
  interface Request {
    myField?: string
  }
  interface Response {
    myField?: string
  }
}
9
JCM

Dans TypeScript, les interfaces sont ouvertes. Cela signifie que vous pouvez leur ajouter des propriétés où que vous soyez en les redéfinissant.

Étant donné que vous utilisez ce fichier express.d.ts , vous devriez pouvoir redéfinir l'interface Request pour ajouter le fichier champ supplémentaire.

interface Request {
  property: string;
}

Ensuite, dans votre fonction middleware, le paramètre req devrait également avoir cette propriété. Vous devriez pouvoir l'utiliser sans aucune modification de votre code.

8
toskv

Une solution possible consiste à utiliser "double casting à tout"

1- définir une interface avec votre propriété

export interface MyRequest extends http.IncomingMessage {
     myProperty: string
}

2- double casting

app.use((req: http.IncomingMessage, res: http.ServerResponse, next: (err?: Error) => void) => {
    const myReq: MyRequest = req as any as MyRequest
    myReq.myProperty = setProperty()
    next()
})

Les avantages de la double coulée sont les suivants:

  • le typage est disponible
  • il ne pollue pas les définitions existantes mais les étend, en évitant toute confusion
  • comme le casting est explicite, il compile les amendes avec le -noImplicitany drapeau

Alternativement, il y a l'itinéraire rapide (non typé):

 req['myProperty'] = setProperty()

(ne modifiez pas les fichiers de définition existants avec vos propres propriétés - cela n'est pas maintenable. Si les définitions sont fausses, ouvrez une demande d'extraction)

EDIT

Voir le commentaire ci-dessous, le casting simple fonctionne dans ce cas req as MyRequest

2
Bruno Grieder

Bien que ce soit une très vieille question, je suis tombé sur ce problème récemment. La réponse acceptée fonctionne bien, mais je devais ajouter une interface personnalisée à Request - une interface que j'utilisais dans mon code et qui ne fonctionnait pas. si bien travailler avec la réponse acceptée. Logiquement, j'ai essayé ceci:

import ITenant from "../interfaces/ITenant";

declare namespace Express {
    export interface Request {
        tenant?: ITenant;
    }
}

Mais cela n’a pas fonctionné car TypeScript traite .d.ts Les fichiers en tant qu'importations globales et lorsqu'ils sont importés, ils sont traités comme des modules normaux. C'est pourquoi le code ci-dessus ne fonctionne pas avec un paramètre TypeScript standard.

Voici ce que j'ai fini par faire

// typings/common.d.ts

declare namespace Express {
    export interface Request {
        tenant?: import("../interfaces/ITenant").default;
    }
}
// interfaces/ITenant.ts

export interface ITenant {
    ...
}
2
16kb