Cette question a été posée plusieurs fois, mais aucune des réponses qui ont été données n'a fonctionné pour moi. J'essaie d'étendre l'objet Request
Express pour inclure une propriété pour stocker un objet User
. J'ai créé un fichier de déclaration, express.d.ts
, et l'ai placé dans le même répertoire que mon tsconfig.json
:
backend/ package.json tsconfig.json express.d.ts src/ models/ user.ts routes/ secured-api.ts
Ensuite, j'essaye de lui faire une affectation dans secured-api.ts
:
{ "compilerOptions": { "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "lib": ["es2015"], /* Specify library files to be included in the compilation. */ "outDir": "./build", /* Redirect output structure to the directory. */ "strict": true, /* Enable all strict type-checking options. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ } }
J'obtiens l'erreur suivante:
import { Model, RelationMappings } from 'objection'; export class User extends Model { public static tableName = 'User'; public static idColumn = 'username'; public static jsonSchema = { type: 'object', required: ['fname', 'lname', 'username', 'email', 'password'], properties: { fname: { type: 'string', minLength: 1, maxLength: 30 }, lname: { type: 'string', minLength: 1, maxLength: 30 }, username: { type: 'string', minLength: 1, maxLength: 20 }, email: { type: 'string', minLength: 1, maxLength: 320 }, password: { type: 'string', minLength: 1, maxLength: 128 }, } }; public static modelPaths = [__dirname]; public static relationMappings: RelationMappings = { }; public fname!: string; public lname!: string; public username!: string; public email!: string; public password!: string; }
Ma classe User
est:
src/routes/secured-api.ts:38:21 - error TS2339: Property 'user' does not exist on type 'Request'. 38 req.user = user; ~~~~
Mon tsconfig.json
est:
import express from 'express'; import { userService } from '../services/user'; router.use(async (req, res, next) => { try { const user = await userService.findByUsername(payload.username); // do stuff to user... req.user = user; next(); } catch(err) { // handle error } });
Ma structure de répertoire est:
import { User } from "./src/models/user"; declare namespace Express { export interface Request { user: User; } }
Qu'est-ce que je fais de mal ici?
4 Réponses :
Le problème est que vous n’augmentez pas l’espace de noms global Express
défini par express
vous créez un nouvel espace de noms dans votre module (le fichier devient un module une fois que vous utilisez une import
).
La solution est de déclarer l'espace de noms dans global
declare namespace Express { export interface Request { user: import("./src/models/user").User; } }
Ou de ne pas utiliser le module syntaxe d'importation, référencez simplement le type:
import { User } from "./src/models/user"; declare global { namespace Express { export interface Request { user: User; } } }
Merci beaucoup! J'ai mis le bloc declare global
dans express.d.ts
, et j'ai dû supprimer le declare
de declare namespace Express code> pour le faire fonctionner, mais cela fonctionne maintenant.
@ user3814613 wops a oublié la déclaration supplémentaire, supprimée. Désolé
Gentil monsieur. Existe-t-il un moyen d'automatiser cela avec tsc? peut-être quand j'ai express_ext.d.ts
dans mon dossier src
, le contenu de express_ext.d.ts
automatiquement à la déclaration globale dans index.d.ts
?
J'ai eu le même problème ...
Vous ne pouvez pas simplement utiliser une requête étendue? comme:
interface RequestWithUser extends Request { user?: User; } router.post('something',(req: RequestWithUser, res: Response, next)=>{ const user = req.user; ... }
Sur une autre note, si vous utilisez des rappels asynchrones avec express, assurez-vous d'utiliser des wrappers express-async ou assurez-vous de savoir exactement ce que vous faites. Je recommande: awaitjs
Je ne suis pas sûr que cela fonctionnera avec strictFunctionTypes
si user
n'est pas facultatif, car le rappel attend un paramètre req
avec plus de clés que post
dit qu'il peut fournir
Oui, cela fonctionnerait aussi, mais l'autre réponse a une solution plus facile à mettre en œuvre. @ TitianCernicova-Dragomir Je ne peux le faire fonctionner qu'en rendant user
facultatif. Si ce n'est pas facultatif, le compilateur se plaint que la fonction ne peut pas être assignée au type PathParams
.
J'utilise actuellement cette solution pour injecter un utilisateur dans la demande à des fins d'authentification et de contrôle d'accès. (L'utilisateur est injecté dans un middleware de passeport). L'utilisateur doit cependant être facultatif ... donc ce n'est pas parfait. Je n'aime pas l'idée d'étendre un autre espace de noms, donc ... je pense que c'est une question de préférence.
Pourquoi le champ user
est-il facultatif dans RequestWithUser? J'ai remarqué que cela ne fonctionnera pas si ce n'est pas facultatif
0
La fusion de déclarations signifie que le compilateur fusionne deux déclarations distinctes déclarées avec le même nom en une seule définition.
Voir l'exemple ci-dessous dans lequel les déclarations d'interface sont fusionnées en typographie
interface Boy { height: number; weight: number; } interface Boy { mark: number; } let boy: Boy = {height: 6 , weight: 50, mark: 50};
J'ai essayé beaucoup de solutions mais aucune d'elles n'a fonctionné.
mais cela peut être aussi simple et fonctionne parfaitement. et bien sûr, vous pouvez enregistrer AuthenticatedRequest n'importe où ailleurs.
declare namespace Express { interface Request { user: import('./src/modules/models/user.model').IUser; uploadedImage: { key: string; type: string }; group: import('./src/modules/models/group.model').IGroup; } }
UPDATE:
J'ai donc trouvé quelque chose d'intéressant d'ici: Importer la classe dans le fichier de définition (* d.ts) . p>
déclare que l'espace de noms Express doit être la première ligne pour le dactylographie le compte comme global
import { IUser } from './src/_models/user.model'; export interface AuthenticatedRequest extends Request { user: IUser; } static async getTicket(req: AuthenticatedRequest, res: Response){}