Typescriptを使用してExpress Requestオブジェクトを拡張する


127

typescriptを使用してミドルウェアからリクエストオブジェクトを表現するプロパティを追加しようとしています。ただし、オブジェクトにプロパティを追加する方法がわかりません。できればブラケット表記は使わない方がいいです。

私はこれに似たものを書くことができる解決策を探しています(可能な場合):

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

express.d.tsファイルが提供するリクエストインターフェースを必要なフィールドで拡張できるはずです。
toskv 2016年

回答:


145

カスタム定義を作成し、TypescriptでDeclaration Mergingと呼ばれる機能を使用したいとします。これは、たとえばで一般的に使用されmethod-overrideます。

ファイルを作成custom.d.tsして、それを含めるようにしてくださいtsconfig.jsonさんfiles-sectionがあれば。内容は次のようになります。

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

これにより、コードの任意の時点で、次のようなものを使用できます。

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)
})

2
私はこれを行ったばかりですが、tsconfig.jsonのfilesセクションにcustom.d.tsファイルを追加しなくても機能しましたが、まだ機能します。これは予想される動作ですか?
Chaim Friedman、

1
@ChaimFriedmanはい。このfilesセクションは、TypeScriptに含まれるファイルのセットを制限します。filesまたはを指定しない場合、デフォルトでincludeすべて*.d.tsが含まれるため、カスタム入力をそこに追加する必要はありません。
interphx 2018年

9
私にとっては機能しません:Property 'tenantタイプ 'Request'には存在しません `明示的に含めても含めなくても、違いはありtsconfig.jsonません。UPDATEdeclare global@basaratのpointetとして彼answear作品に出て、私はしなければならなかったimport {Request} from 'express'最初の。
ライオン

5
FWIW、この回答は現在廃止されています。JCMの答えは、Requestexpressjsでオブジェクトを拡張する正しい方法です(少なくとも4.x)
Eric Liprandi '25 / 07/25

3
将来の検索のために-私はすぐにうまくいった良い例を見つけました:github.com/3mard/ts-node-example
jd291

79

コメントでindex.d.ts示唆されているように、グローバルExpressネームスペースに新しいメンバーを宣言するだけです。例:

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

完全な例:

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

グローバル名前空間の拡張については、私のGitBookで詳しく説明しています


宣言でグローバルが必要なのはなぜですか?ない場合はどうなりますか?
Jason Kuhrt、2018年

:タイプが「閉」であり、マージすることができない、なお、マージタイプへのインタフェースが、ケースの誰もニーズを持つこの作品github.com/Microsoft/TypeScript/issues/...
ピーターWを

@basaratさんビールをお借りしています。
marcellsimon

tsconfig.jsonにも追加する必要がありました:{"compilerOptions":{"typeRoots":["./src/typings/"、 "./node_modules/@types"]}、 "files":["./ src / typings / express / index.d.ts "]}
marcellsimon

上記の解決策はどれもうまくいきませんでしたが、これは最初の実行でうまくいきました。ありがとう
リテッシュ

55

新しいバージョンのExpressでは、express-serve-static-coreモジュールを拡張する必要があります。

これが必要になるのは、Expressオブジェクトがそこから取得されるためです。https//github.com/DefinitelyTyped/DefinitelyTyped/blob/8fb0e959c2c7529b5fa4793a44b41b797ae671b9/types/express/index.d.ts#L19

基本的には、以下を使用します。

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

1
これは私にとってはうまくいきましたが、普通の古い'express'モジュールを拡張するとうまくいきませんでした。ありがとうございました!
ベンクリーガー、

4
:仕事にこれを取得するために、私は同様のモジュールをインポートする必要がありました、これに苦しんでいたimport {Express} from "express-serve-static-core";
andre_b

1
@andre_bヒントをありがとう。import文はファイルをモジュールに変えると思います、そしてこれは必要な部分です。私はexport {}これも機能するものを使用するように切り替えました。
Danyal Aytekin

2
このコードが入っているファイルが呼び出されていないことを確認してください。呼び出されていないexpress.d.ts場合、コンパイラーはこれをエクスプレスタイピングにマージしようとし、エラーが発生します。
トムスペンサー、

3
タイプがtypeRootsの最初にあることを確認してください!types / express / index.d.ts and tsconfig => "typeRoots":["./src/types"、 "./node_modules/@types"]
kaya

30

(他のように)受け入れられた答えは私にはうまくいきませんが

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

した。それが誰かを助けることを願っています。


2
同様の方法は、ts docsの「Module Augmentation」で説明されています。*.d.tsファイルを使用したくない場合は、通常の*.tsファイル内にタイプを保存するだけで十分です。
im.pankratov

3
これは私にとってもうまくいった唯一のことであり、他のすべての答えは.d.tsファイルにある必要があるようです
議会

custom-declarations.d.tsTypeScriptのプロジェクトルートにファイルを配置した場合、これは私にとっても機能します。
focorner

元のタイプを拡張して保持しました:import { Request as IRequest } from 'express/index';およびinterface Request extends IRequest。typeRoot
Ben Creasy

17

8かそこらの答えを試してみましたが、成功していません。3mardsリポジトリを指し示すjd291のコメントを使用して、なんとか動作させることができました。

と呼ばれるベースにファイルを作成しますtypes/express/index.d.ts。そしてそれに書いてください:

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

そしてそれを以下に含めますtsconfig.json

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

次にyourProperty、すべてのリクエストでアクセスできるようにする必要があります。

import express from 'express';

const app = express();

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

14

提供された解決策はどれも私にとってうまくいきませんでした。最終的には、Requestインターフェースを単純に拡張しました。

import {Request} from 'express';

export interface RequestCustom extends Request
{
    property: string;
}

それを使用するには:

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

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

編集:TypeScriptの最近のバージョンでは、これについて不満があります。代わりに、私はしなければなりませんでした:

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

1
動作しますが、ミドルウェア機能が100種類ある場合は非常に冗長です。amirite–
Alexander Mills

1
@ user2473015ええ、Typescriptの最近のバージョンはこれを壊しました。私の更新された答えを見てください。
Tom Mettam

8

TypeScriptでは、インターフェースはオープンエンドです。つまり、プロパティを再定義するだけで、どこからでもプロパティを追加できます。

このexpress.d.tsファイルを使用していることを考えると、Requestインターフェースを再定義して、追加のフィールドを追加できるはずです。

interface Request {
  property: string;
}

次に、ミドルウェア関数では、reqパラメーターにもこのプロパティが必要です。コードを変更せずに使用できるはずです。


1
コード全体でその情報をどのように「共有」しますか?リクエストでプロパティを定義する場合Request.user = {};app.tsどのようにuserController.tsそれを知るのですか?
Nepoxx

2
@Nepoxxインターフェースを再定義すると、コンパイラーはプロパティーをマージし、どこにでも表示できるようになります。それが理由です。理想的には、.d.tsファイルで再定義を行います。:)
toskv

それは機能しているようですが、タイプを使用する場合express.Handler(手動で指定するの(req: express.Request, res: express.Response, next: express.NextFunction) => any)ではなく)、Requestプロパティが存在しないというメッセージが表示されるため、同じタイプを参照していないようです。
Nepoxx

express.HandlerがRequestインターフェースを拡張しない限り、私はそれを期待していません。しますか?
toskv

2
私が使用すればそれを機能させることができますdeclare module "express"が、使用してもできませんdeclare namespace Express。名前空間構文を使用したいのですが、うまくいきません。
WillyC、2016

5

これは非常に古い質問ですが、最近この問題に遭遇しました。承認された回答は問題なく機能しますが、カスタムインターフェイスを追加する必要Requestがありました。回答。論理的に、私はこれを試しました:

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

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

しかし、Typescriptは.d.tsファイルをグローバルインポートとして扱い、インポートがある場合は通常のモジュールとして扱われるため、これは機能しませんでした。上記のコードが標準のtypescript設定で機能しないのはそのためです。

これが私がやったことです

// typings/common.d.ts

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

export interface ITenant {
    ...
}

これは私のメインファイルでは機能しますが、ルーティングファイルまたはコントローラーでは機能しません。リンティングは表示されませんが、コンパイルしようとすると、「プロパティ 'user'はタイプ 'Request'に存在しません」と表示されます。(私はテナントの代わりにユーザーを使用しています)しかし、// @ ts-ignoreをその上に追加した場合、それは機能します(もちろん、それはそれを修正する愚かな方法です。私の他のファイルのために働いていますか?
ローガン

それは@Loganの非常に奇妙なことです。あなたを共有することができ.d.tstsconfig.jsonかつ使用インスタンス?さらに、グローバルモジュールでのこのインポートはTS 2.9以降でのみサポートされているため、使用しているtypescriptのバージョンは何ですか?それはより良い助けになるでしょう。
16kb

私は、ここにデータをアップロードしたpastebin.com/0npmR1Zrハイライトが全て台無しにされた理由は、メインファイルからですが、私はわからないんだけどprnt.sc/n6xsyl これは、別のファイルからであるprnt.sc/n6xtp0 明らかにいくつかの部分何が起こっているのかは理解していますが、コンパイラーは理解していません。typescriptのバージョン3.2.2を使用しています
Logan

1
驚いたことに、 ... "include": [ "src/**/*" ] ...私にとって"include": ["./src/", "./src/Types/*.d.ts"],は機能しますが、機能しません。私はまだこれを理解しようとすることに
専念し

動的インポートを使用したインターフェースのインポートは私にとってはうまくいきます。ありがとう
Roman Mahotskyi

3

たぶんこの問題は解決されたかもしれませんが、私は少しだけ共有したいと思います。他の回答のようなインターフェースが制限が多すぎる場合がありますが、実際に必要なプロパティを維持し、追加のプロパティを追加して追加することができますstring値のタイプがのタイプのキーany

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();
});

そのため、このオブジェクトに追加するプロパティを追加することもできます。


2

あなたがexpress4で動作するソリューションを探しているなら、ここにあります:

@ types / express / index.d.ts:-------- /index.d.tsである必要があります

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",
    ]
  }
}

https://github.com/TypeStrong/ts-node/issues/715#issuecomment-526757308からの参照


2

これらの応答はすべて間違っているか、何らかの形で古くなっています。

これは2020年5月に私のために働きました:

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

import * as express from "express"

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

tsconfig.json、次のようなプロパティを追加/マージします。

"typeRoots": [ "@types" ]

乾杯。


Webpack + Dockerで動作し、import *をexport {}に置き換えることができます。
Dooomel

1

考えられる解決策の1つは、「任意へのダブルキャスト」を使用することです。

1-プロパティとのインターフェースを定義する

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

2-ダブルキャスト

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

ダブルキャスティングの利点は次のとおりです。

  • タイピングが利用可能です
  • 既存の定義を汚染することはありませんが、それらを拡張して混乱を回避します
  • キャストは明示的であるため、-noImplicitanyフラグで罰金をコンパイルします

または、クイック(タイプなし)ルートもあります。

 req['myProperty'] = setProperty()

(独自のプロパティで既存の定義ファイルを編集しないでください-これは保守不可能です。定義が間違っている場合は、プルリクエストを開きます)

編集

以下のコメントを参照してください。この場合、単純なキャストが機能します req as MyRequest


この場合@akshayは、はい、なぜならMyRequest拡張しますhttp.IncomingMessage。それが事実ではなかった場合、バイアキャスティングanyが唯一の選択肢になります
Bruno Grieder '30

anyではなくunknownにキャストすることをお勧めします。
開発

0

この回答は、npmパッケージに依存しているユーザーにとって有益ts-nodeです。

リクエストオブジェクトを拡張するという同じ懸念にも苦労していました。スタックオーバーフローで多くの回答を追跡し、以下の戦略に従って終了しました。

以下のディレクトリでexpressの拡張型宣言を宣言しました。${PROJECT_ROOT}/api/@types/express/index.d.ts

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

次に、私tsconfig.jsonをこのようなものに更新します。

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

上記の手順を実行した後でも、Visual Studioは文句を言うのをやめましたが、残念ながら、ts-nodeコンパイラーはスローを使用していました。

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

どうやら、リクエストのts-node拡張タイプ定義を見つけることができませんでしたオブジェクトの。

最終的に何時間も費やした後、VSコードは文句を言わず、型定義を見つけることができたので、ts-nodeコンパイラーに何か問題があることを暗示していました。

更新開始scriptpackage.json修正しました。

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

--files引数は、ここで重要な役割を果たしてカスタム型の定義を決定する見つけます。

詳細については、https//github.com/TypeStrong/ts-node#help-my-types-are-missingをご覧ください。


0

何か他のものを探しているだけの人がここで試すのを手伝うことは、2020年5月下旬にExpressJSのリクエストを拡張しようとしたときに私に役立ちました。これを機能させる前に、私はダース以上のものを試さなければなりませんでした:

  • tsconfig.jsonの「typeRoots」で誰もが推奨している順序を反転します(「./src」などのtsconfigにrootDir設定がある場合は、srcパスを削除することを忘れないでください)。例:
"typeRoots": [
      "./node_modules/@types",
      "./your-custom-types-dir"
]
  • カスタム拡張の例( './your-custom-types-dir/express/index.d.ts ")インラインインポートとデフォルトエクスポートを使用して、クラスをタイプとして使用する必要がありました。
declare global {
  namespace Express {
    interface Request {
      customBasicProperty: string,
      customClassProperty: import("../path/to/CustomClass").default;
    }
  }
}
  • nodemon.jsonファイルを更新して、「-files」コマンドをts-nodeに追加します。例:
{
  "restartable": "rs",
  "ignore": [".git", "node_modules/**/node_modules"],
  "verbose": true,
  "exec": "ts-node --files",
  "watch": ["src/"],
  "env": {
    "NODE_ENV": "development"
  },
  "ext": "js,json,ts"
}

0

すでにこの回答にはかなり遅れているかもしれませんが、とにかくここに私が解決した方法があります:

  1. タイプソースがtsconfigファイルに含まれていることを確認してください(これはまったく新しいスレッドである可能性があります)
  2. タイプディレクトリ内に新しいディレクトリを追加し、タイプを拡張または作成するパッケージとして名前を付けます。この特定のケースでは、次の名前のディレクトリを作成しますexpress
  3. expressディレクトリ内にファイルを作成して名前を付けますindex.d.ts()
  4. 最後に、型の拡張を作成するには、次のようなコードを配置する必要があります。
declare module 'express' {
    export interface Request {
        property?: string;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.