Erweitern Sie das Express Request-Objekt mit Typescript

125

Ich versuche, eine Eigenschaft hinzuzufügen, um das Anforderungsobjekt einer Middleware mithilfe von Typoskript auszudrücken. Ich kann jedoch nicht herausfinden, wie dem Objekt zusätzliche Eigenschaften hinzugefügt werden können. Ich würde es vorziehen, wenn möglich keine Klammernotation zu verwenden.

Ich suche nach einer Lösung, mit der ich (wenn möglich) etwas Ähnliches schreiben kann:

app.use((req, res, next) => {
    req.property = setProperty(); 
    next();
});
Isak Ågren
quelle
Sie sollten in der Lage sein, die Anforderungsschnittstelle, die die Datei express.d.ts bereitstellt, um die gewünschten Felder zu erweitern.
toskv

Antworten:

142

Sie möchten eine benutzerdefinierte Definition erstellen und in Typescript eine Funktion namens Declaration Merging verwenden . Dies wird üblicherweise verwendet, z method-override.

Erstellen Sie eine Datei custom.d.tsund stellen Sie sicher , es schließen in Ihrem tsconfig.json‚s files-Abschnitt falls vorhanden. Der Inhalt kann wie folgt aussehen:

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

Auf diese Weise können Sie zu jedem Zeitpunkt in Ihrem Code Folgendes verwenden:

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)
})
maximilianvp
quelle
2
Ich habe das gerade getan, aber ich habe es zum Laufen gebracht, ohne meine Datei custom.d.ts zum Dateibereich in meiner Datei tsconfig.json hinzuzufügen, aber es funktioniert immer noch. Ist das erwartetes Verhalten?
Chaim Friedman
1
@ChaimFriedman Ja. Der filesAbschnitt schränkt den Satz von Dateien ein, die in TypeScript enthalten sind. Wenn Sie nicht angeben , filesoder include, dann werden alle *.d.tsstandardmäßig enthalten sind, so gibt es keine Notwendigkeit , Ihre individuellen Typisierungen hinzuzufügen gibt.
Interphx
9
Funktioniert nicht für mich: Ich bekomme Property 'tenantnicht existiert für Typ 'Anfrage' `Macht keinen Unterschied, ob ich es explizit in einbinde tsconfig.jsonoder nicht. UPDATE Mit declare globalwie @basarat in seinen Answear-Arbeiten hervorhebt, musste ich das import {Request} from 'express'erstmal machen.
Löwe
5
FWIW, diese Antwort ist jetzt veraltet . JCMs Antwort ist der richtige Weg, um das RequestObjekt in Expressjs (mindestens 4.x) zu erweitern
Eric Liprandi
3
Für zukünftige Suchvorgänge - ein gutes Beispiel, das
sofort
79

Wie aus den Kommentaren in der hervorgehtindex.d.ts , deklarieren Sie einfach Expressalle neuen Mitglieder im globalen Namespace. Beispiel:

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

Vollständiges Beispiel:

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!'))

Das Erweitern globaler Namespaces wird in meinem GitBook ausführlicher behandelt .

Basarat
quelle
Warum wird in der Erklärung global benötigt? Was passiert, wenn es nicht da ist?
Jason Kuhrt
Dies funktioniert mit Schnittstellen, aber falls jemand Typen zusammenführen muss, beachten Sie, dass Typen "geschlossen" sind und nicht zusammengeführt werden können: github.com/Microsoft/TypeScript/issues/…
Peter W
Mr @basarat Ich schulde Ihnen ein paar Biere.
Marcellsimon
Ich musste auch zu meiner tsconfig.json hinzufügen: {"compilerOptions": {"typeRoots": ["./src/typings/", "./node_modules/@types"]}, "files": ["./ src / typings / express / index.d.ts "]}
marcellsimon
Keine der oben genannten Lösungen hat funktioniert .. aber diese hat den Job im ersten Durchgang gemacht .. vielen Dank .. !!
Ritesh
55

Für neuere Express-Versionen müssen Sie das express-serve-static-coreModul erweitern.

Dies ist erforderlich, da das Express-Objekt jetzt von dort stammt: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8fb0e959c2c7529b5fa4793a44b41b797ae671b9/types/express/index.d.ts#L19

Verwenden Sie grundsätzlich Folgendes:

declare module 'express-serve-static-core' {
  interface Request {
    myField?: string
  }
  interface Response {
    myField?: string
  }
}
JCM
quelle
1
Dies funktionierte für mich, während die Erweiterung des einfachen alten 'express'Moduls dies nicht tat. Danke dir!
Ben Kreeger
4
Hatte damit zu kämpfen, um dies zum import {Express} from "express-serve-static-core";
Laufen
1
@andre_b Danke für den Hinweis. Ich denke, dass die import-Anweisung die Datei in ein Modul verwandelt, und dies ist der Teil, der notwendig ist. Ich habe auf die Verwendung umgestellt, export {}die auch funktioniert.
Danyal Aytekin
2
Stellen Sie sicher, dass die Datei, in die dieser Code eingefügt wird, nicht aufgerufen express.d.tswird. Andernfalls versucht der Compiler, diese Datei mit den Express-Typisierungen zusammenzuführen, was zu Fehlern führt.
Tom Spencer
3
Stellen Sie sicher, dass Ihre Typen in typeRoots an erster Stelle stehen müssen! types / express / index.d.ts und tsconfig => "typeRoots": ["./src/types", "./node_modules/@types"]
kaya
30

Die akzeptierte Antwort (wie die anderen) funktioniert bei mir aber nicht

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

tat. Hoffe das wird jemandem helfen.

max-lt
quelle
2
Eine ähnliche Methode wird in den Dokumenten unter "Modulerweiterung" beschrieben. Es ist großartig, wenn Sie keine *.d.tsDateien verwenden und Ihre Typen nur in regulären *.tsDateien speichern möchten .
im.pankratov
3
Dies ist das einzige, was auch für mich funktioniert hat. Alle anderen Antworten scheinen in .d.ts-Dateien zu sein
Parlament
Dies funktioniert auch für mich, vorausgesetzt, ich lege meine custom-declarations.d.tsDatei in das Projektstammverzeichnis von TypeScript.
Focorner
Ich habe den ursprünglichen Typ erweitert, um ihn zu erhalten: import { Request as IRequest } from 'express/index';und interface Request extends IRequest. Musste auch den typeRoot
Ben Creasy
17

Nach 8 oder so Antworten und ohne Erfolg. Ich habe es endlich geschafft, es mit jd291s Kommentar zum 3mards Repo zum Laufen zu bringen .

Erstellen Sie eine Datei in der Basis mit dem Namen types/express/index.d.ts. Und darin schreiben:

declare namespace Express {
    interface Request {
        yourProperty: <YourType>;
    }
}

und fügen Sie es hinzu tsconfig.jsonmit:

{
    "compilerOptions": {
        "typeRoots": ["./types"]
    }
}

Dann yourPropertysollte unter jeder Anfrage zugänglich sein:

import express from 'express';

const app = express();

app.get('*', (req, res) => {
    req.yourProperty = 
});
Kaleidawave
quelle
14

Keine der angebotenen Lösungen hat bei mir funktioniert. Am Ende habe ich einfach die Anforderungsschnittstelle erweitert:

import {Request} from 'express';

export interface RequestCustom extends Request
{
    property: string;
}

Dann, um es zu benutzen:

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

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

Bearbeiten : Neuere Versionen von TypeScript beschweren sich darüber. Stattdessen musste ich tun:

someMiddleware(expressRequest: Request, res: Response, next: NextFunction): void
{
    const req = expressRequest as RequestCustom;
    req.property = '';
}
Tom Mettam
quelle
1
das wird funktionieren, aber ziemlich ausführlich, wenn Sie Hunderte von Middleware-Funktionen haben, amirite
Alexander Mills
1
@ user2473015 Ja, neuere Versionen von Typescript haben dies gebrochen. Siehe meine aktualisierte Antwort.
Tom Mettam
8

In TypeScript sind Schnittstellen offen. Das heißt, Sie können ihnen von überall Eigenschaften hinzufügen, indem Sie sie einfach neu definieren.

Wenn Sie diese express.d.ts- Datei verwenden, sollten Sie die Anforderungsschnittstelle neu definieren können, um das zusätzliche Feld hinzuzufügen.

interface Request {
  property: string;
}

In Ihrer Middleware-Funktion sollte der Parameter req ebenfalls diese Eigenschaft haben. Sie sollten es ohne Änderungen an Ihrem Code verwenden können.

toskv
quelle
1
Wie "teilen" Sie diese Informationen in Ihrem Code? Wenn ich eine Immobilie in Antrag definieren, sagen Request.user = {};in app.tswie funktioniert userController.tses wissen?
Nepoxx
2
@Nepoxx Wenn Sie eine Schnittstelle neu definieren, führt der Compiler die Eigenschaften zusammen und macht sie überall sichtbar. Deshalb. Idealerweise führen Sie die Neudefinition in einer .d.ts-Datei durch. :)
toskv
Das scheint zu funktionieren, aber wenn ich den Typ verwende express.Handler(anstatt ihn manuell anzugeben (req: express.Request, res: express.Response, next: express.NextFunction) => any)), scheint er sich nicht auf denselben zu beziehen, Requestda er sich beschwert, dass meine Eigenschaft nicht existiert.
Nepoxx
Ich würde es nicht erwarten, es sei denn, express.Handler erweitert die Anforderungsschnittstelle. macht es?
toskv
2
Ich kann das zum Laufen bringen, wenn ich es benutze, declare module "express"aber nicht, wenn ich es benutze declare namespace Express. Ich würde lieber die Namespace-Syntax verwenden, aber sie funktioniert bei mir einfach nicht.
WillyC
5

Obwohl dies eine sehr alte Frage ist, bin ich in letzter Zeit auf dieses Problem gestoßen. Die akzeptierte Antwort funktioniert in Ordnung, aber ich musste eine benutzerdefinierte Schnittstelle hinzufügen Request- eine Schnittstelle, die ich in meinem Code verwendet hatte und die mit der akzeptierten nicht so gut funktionierte Antworten. Logischerweise habe ich Folgendes versucht:

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

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

Dies hat jedoch nicht funktioniert, da Typescript .d.tsDateien als globale Importe behandelt und wenn sie Importe enthalten, werden sie als normale Module behandelt. Aus diesem Grund funktioniert der obige Code bei einer Standardeinstellung für Typoskripte nicht.

Folgendes habe ich getan

// typings/common.d.ts

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

export interface ITenant {
    ...
}
16kb
quelle
Dies funktioniert für meine Hauptdatei, aber nicht für meine Routing-Dateien oder Controller. Ich erhalte kein Flusen. Wenn ich jedoch versuche, es zu kompilieren, wird angezeigt, dass "Eigenschaft 'Benutzer' für Typ 'Anfrage' nicht vorhanden ist." (Ich verwende Benutzer anstelle von Mandant), aber wenn ich // @ ts-ignore über ihnen hinzufüge, funktioniert es (obwohl dies natürlich eine dumme Methode ist, dies zu beheben. Haben Sie irgendwelche Gedanken darüber, warum dies möglicherweise nicht der Fall ist? Arbeiten für meine anderen Dateien?
Logan
Das ist eine sehr seltsame Sache @Logan. Können Sie Ihre teilen .d.ts, tsconfig.jsonund die Verwendung Beispiel? Welche Version von Typoskript verwenden Sie, da dieser Import in globale Module nur ab TS 2.9 unterstützt wird? Das könnte besser helfen.
16kb
Ich habe Daten hier hochgeladen, pastebin.com/0npmR1Zr Ich bin mir nicht sicher , warum die Markierung alle durcheinander ist , obwohl diese von der Hauptdatei ist prnt.sc/n6xsyl Diese aus einer anderen Datei ist prnt.sc/n6xtp0 eindeutig ein Teil von es versteht, was los ist, aber der Compiler nicht. Ich benutze Version 3.2.2 von Typoskript
Logan
1
Überraschenderweise ... "include": [ "src/**/*" ] ...funktioniert es für mich, "include": ["./src/", "./src/Types/*.d.ts"],aber nicht. Ich habe noch nicht versucht, dies zu verstehen
16kb
Das Importieren der Benutzeroberfläche mithilfe dynamischer Importe funktioniert bei mir. Danke
Roman Mahotskyi
3

Vielleicht wurde dieses Problem beantwortet, aber ich möchte nur ein wenig teilen. Jetzt kann eine Schnittstelle wie andere Antworten manchmal etwas zu restriktiv sein, aber wir können die erforderlichen Eigenschaften tatsächlich beibehalten und dann alle zusätzlichen Eigenschaften hinzufügen, die durch Erstellen eines hinzugefügt werden sollen Schlüssel mit einem Typ von stringmit Wert Typ vonany

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

interface IRequest extends Request {
  [key: string]: any
}

app.use( (req: IRequest, res: Response, next: NextFunction) => {
  req.property = setProperty();

  next();
});

Jetzt können wir diesem Objekt auch jede weitere Eigenschaft hinzufügen, die wir möchten.

Eka Putra
quelle
2

Wenn Sie nach einer Lösung suchen, die mit express4 funktioniert, finden Sie hier:

@ types / express / index.d.ts: -------- muss /index.d.ts sein

declare namespace Express { // must be namespace, and not declare module "Express" { 
  export interface Request {
    user: any;
  }
}

tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2016",
    "typeRoots" : [
      "@types", // custom merged types must be first in a list
      "node_modules/@types",
    ]
  }
}

Siehe https://github.com/TypeStrong/ts-node/issues/715#issuecomment-526757308

Chandara Chea
quelle
2

Alle diese Antworten scheinen auf die eine oder andere Weise falsch oder veraltet zu sein.

Das hat bei mir im Mai 2020 funktioniert:

in ${PROJECT_ROOT}/@types/express/index.d.ts:

import * as express from "express"

declare global {
    namespace Express {
        interface Request {
            my_custom_property: TheCustomType
        }
    }
}

in tsconfig.json, Add / fusionieren die Eigenschaft, dass:

"typeRoots": [ "@types" ]

Prost.

Will Brickner
quelle
Funktioniert mit Webpack + Docker. Import * kann durch export {} ersetzt werden.
Dooomel
1

Eine mögliche Lösung ist die Verwendung von "Double Casting to any".

1- Definieren Sie eine Schnittstelle zu Ihrer Immobilie

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

2- Doppelbesetzung

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

Die Vorteile des Doppelgusses sind:

  • Schreibvorgänge sind verfügbar
  • Bestehende Definitionen werden nicht verschmutzt, sondern erweitert, um Verwirrung zu vermeiden
  • Da das Casting explizit ist, werden Bußgelder mit der -noImplicitanyFlagge zusammengestellt

Alternativ gibt es die schnelle (untypisierte) Route:

 req['myProperty'] = setProperty()

(Bearbeiten Sie vorhandene Definitionsdateien nicht mit Ihren eigenen Eigenschaften - dies ist nicht wartbar. Wenn die Definitionen falsch sind, öffnen Sie eine Pull-Anforderung.)

BEARBEITEN

Siehe Kommentar unten, einfaches Casting funktioniert in diesem Fall req as MyRequest

Bruno Grieder
quelle
@akshay In diesem Fall ja, weil MyRequesterweitert die http.IncomingMessage. Wäre dies nicht der Fall, wäre Double Casting via anydie einzige Alternative
Bruno Grieder
Es wird empfohlen, dass Sie anstelle von "Unbekannt" verwenden.
dev
0

Diese Antwort ist für diejenigen von Vorteil, die sich auf das npm-Paket verlassen ts-node.

Ich hatte auch mit dem gleichen Problem zu kämpfen, das Anforderungsobjekt zu erweitern . Ich folgte vielen Antworten im Stapelüberlauf und folgte schließlich der unten genannten Strategie.

Ich habe die erweiterte Eingabe für Express im folgenden Verzeichnis deklariert .${PROJECT_ROOT}/api/@types/express/index.d.ts

declare namespace Express {
  interface Request {
    decoded?: any;
  }
}

dann aktualisiere ich tsconfig.jsonauf so etwas.

{
  "compilerOptions": {
     "typeRoots": ["api/@types", "node_modules/@types"]
      ...
  }
}

Selbst nachdem die obigen Schritte ausgeführt wurden, hörte das Visual Studio auf, sich zu beschweren, aber leider ts-nodewarf der Compiler immer noch.

 Property 'decoded' does not exist on type 'Request'.

Anscheinend ts-nodekonnte der die erweiterten Typdefinitionen für die Anfrage nicht finden .

Nachdem ich Stunden verbracht hatte, beschwerte ich mich, dass der VS-Code sich nicht beschwerte und die Typisierungsdefinitionen finden konnte, was bedeutet, dass etwas mit der ts-nodeKonformität nicht stimmt.

Aktualisierungsstart scriptin package.jsonbehoben für mich.

"start": "ts-node --files api/index.ts",

Die --filesArgumente spielen hier eine Schlüsselrolle bei der Bestimmung der benutzerdefinierten Typdefinitionen.

Weitere Informationen finden Sie unter: https://github.com/TypeStrong/ts-node#help-my-types-are-missing

Divyanshu Rawat
quelle
0

Allen zu helfen, die nur nach etwas anderem suchen, um es hier zu versuchen, hat mir Ende Mai 2020 geholfen, als ich versuchte, die Anfrage von ExpressJS zu verlängern. Ich musste mehr als ein Dutzend Dinge ausprobiert haben, bevor dies funktioniert:

  • Drehen Sie die Reihenfolge um, die jeder in den "typeRoots" Ihrer Datei tsconfig.json empfiehlt (und vergessen Sie nicht, den src-Pfad zu löschen, wenn Sie in tsconfig eine rootDir-Einstellung wie "./src" haben). Beispiel:
"typeRoots": [
      "./node_modules/@types",
      "./your-custom-types-dir"
]
  • Beispiel für eine benutzerdefinierte Erweiterung ('./your-custom-types-dir/express/index.d.ts "). Ich musste Inline-Import und Standardexporte verwenden, um meiner Erfahrung nach Klassen als Typ zu verwenden, damit auch Folgendes gezeigt wird:
declare global {
  namespace Express {
    interface Request {
      customBasicProperty: string,
      customClassProperty: import("../path/to/CustomClass").default;
    }
  }
}
  • Aktualisieren Sie Ihre Datei nodemon.json, um den Befehl "--files" zu ts-node hinzuzufügen. Beispiel:
{
  "restartable": "rs",
  "ignore": [".git", "node_modules/**/node_modules"],
  "verbose": true,
  "exec": "ts-node --files",
  "watch": ["src/"],
  "env": {
    "NODE_ENV": "development"
  },
  "ext": "js,json,ts"
}
Wenn wahr
quelle
0

Es mag für diese Antwort schon ziemlich spät sein, aber trotzdem habe ich Folgendes gelöst:

  1. Stellen Sie sicher, dass Ihre Typquelle in Ihrer tsconfigDatei enthalten ist (dies könnte ein ganz neuer Thread sein).
  2. Fügen Sie in Ihrem Typverzeichnis ein neues Verzeichnis hinzu und benennen Sie es als das Paket, für das Sie Typen erweitern oder erstellen möchten. In diesem speziellen Fall erstellen Sie ein Verzeichnis mit dem Namenexpress
  3. expressErstellen Sie innerhalb des Verzeichnisses eine Datei und benennen Sie sie index.d.ts(MUSS GENAU WIE DAS SEIN)
  4. Um die Erweiterung der Typen vorzunehmen, müssen Sie nur einen Code wie den folgenden eingeben:
declare module 'express' {
    export interface Request {
        property?: string;
    }
}
Hector Manuel
quelle