Perluas objek Permintaan Ekspres menggunakan Skrip Ketik


128

Saya mencoba menambahkan properti untuk mengekspresikan objek permintaan dari middleware menggunakan skrip ketikan. Namun saya tidak tahu bagaimana menambahkan properti tambahan ke objek. Saya lebih suka tidak menggunakan notasi braket jika memungkinkan.

Saya mencari solusi yang memungkinkan saya menulis sesuatu yang mirip dengan ini (jika memungkinkan):

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

Anda harus dapat memperluas antarmuka permintaan yang disediakan oleh file express.d.ts dengan bidang yang Anda inginkan.
toskv

Jawaban:


147

Anda ingin membuat definisi kustom, dan menggunakan fitur di Typecript yang disebut Penggabungan Deklarasi . Ini biasanya digunakan, misalnya dalam method-override.

Buat file custom.d.tsdan pastikan untuk memasukkannya ke dalam Anda tsconfig.json's files-bagian jika ada. Isinya bisa terlihat sebagai berikut:

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

Ini akan memungkinkan Anda, kapan pun dalam kode Anda, menggunakan sesuatu seperti ini:

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
Saya baru saja melakukan ini, tetapi saya berhasil tanpa menambahkan file custom.d.ts saya ke bagian file di tsconfig.json saya, namun tetap berfungsi. Apakah ini perilaku yang diharapkan?
Chaim Friedman

1
@Yayasan_muda. The filesBagian membatasi set file disertakan oleh naskah. Jika Anda tidak menentukan filesatau include, maka semua *.d.tsdisertakan secara default, jadi tidak perlu menambahkan pengetikan kustom Anda di sana.
interphx

9
Tidak bekerja untuk saya: Saya mendapatkan Property 'tenanttidak ada pada jenis 'Permintaan' `` Tidak ada bedanya jika saya secara eksplisit memasukkannya tsconfig.jsonatau tidak. UPDATE Dengan declare globalseperti yang ditunjukkan @basarat di jawaban dia, tetapi saya harus melakukannya import {Request} from 'express'terlebih dahulu.
Singa

5
FWIW, jawaban ini sekarang sudah usang . Jawaban JCM adalah cara yang benar untuk menambah Requestobjek dalam expressjs (setidaknya 4.x)
Eric Liprandi

3
Untuk penelusuran di masa mendatang - contoh bagus yang saya temukan berhasil di luar kotak: github.com/3mard/ts-node-example
jd291

79

Seperti yang disarankan oleh komentar diindex.d.ts , Anda cukup mendeklarasikan ke Expressnamespace global setiap anggota baru. Contoh:

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

Contoh Lengkap:

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

Memperluas namespace global dibahas lebih lanjut di GitBook saya .


Mengapa global dibutuhkan dalam deklarasi? Apa yang terjadi jika tidak ada?
Jason Kuhrt

Ini bekerja dengan interface, tetapi dalam kasus orang kebutuhan untuk jenis merge, catatan bahwa jenis yang "tertutup" dan tidak dapat digabungkan: github.com/Microsoft/TypeScript/issues/...
Peter W

Mr @basarat Aku berhutang budi padamu.
marcellsimon

Saya juga harus menambahkan ke tsconfig.json saya: {"compilerOptions": {"typeRoots": ["./src/typings/", "./node_modules/@types"]}, "files": ["./ src / typings / express / index.d.ts "]}
marcellsimon

Tidak ada solusi di atas yang berhasil .. tetapi yang ini berhasil saat dijalankan pertama kali .. terima kasih banyak .. !!
Ritesh

55

Untuk versi express yang lebih baru, Anda perlu menambah express-serve-static-coremodul.

Ini diperlukan karena sekarang objek Express berasal dari sana: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8fb0e959c2c7529b5fa4793a44b41b797ae671b9/types/express/index.d.ts#L19

Pada dasarnya, gunakan yang berikut ini:

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

1
Ini berhasil untuk saya, sedangkan memperpanjang 'express'modul lama tidak. Terima kasih!
Ben Kreeger

4
Sedang berjuang dengan ini, agar ini berfungsi, saya harus mengimpor modul juga:import {Express} from "express-serve-static-core";
andre_b

1
@andre_b Terima kasih atas petunjuknya. Saya pikir pernyataan import mengubah file menjadi modul, dan ini adalah bagian yang diperlukan. Saya telah beralih menggunakan export {}yang juga berfungsi.
Danyal Aytekin

2
Pastikan file tempat kode ini masuk tidak dipanggil express.d.ts, jika tidak kompilator akan mencoba menggabungkan ini ke dalam pengetikan ekspres, mengakibatkan kesalahan.
Tom Spencer

3
Pastikan tipe Anda harus menjadi yang pertama di typeRoots! types / express / index.d.ts dan tsconfig => "typeRoots": ["./src/types", "./node_modules/@types"]
kaya

31

Jawaban yang diterima (seperti yang lain) tidak berhasil untuk saya tetapi

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

melakukan. Harapan itu akan membantu seseorang.


2
Metode serupa dijelaskan dalam dokumen ts di bawah "Augmentasi Modul". Sangat bagus jika Anda tidak ingin menggunakan *.d.tsfile dan hanya menyimpan tipe Anda dalam *.tsfile biasa .
im.pankratov

3
ini adalah satu-satunya hal yang berhasil untuk saya juga, semua jawaban lain tampaknya perlu ada di file .d.ts
parlemen

Ini berfungsi untuk saya juga, asalkan saya meletakkan custom-declarations.d.tsfile saya di root proyek TypeScript.
focorner

Saya memperluas tipe asli untuk melestarikannya: import { Request as IRequest } from 'express/index';dan interface Request extends IRequest. Juga harus menambahkan typeRoot
Ben Creasy

17

Setelah mencoba 8 atau lebih jawaban dan tidak berhasil. Saya akhirnya berhasil membuatnya bekerja dengan jd291 's komentar menunjuk ke 3mards repo .

Buat file di basis bernama types/express/index.d.ts. Dan di dalamnya tertulis:

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

dan memasukkannya ke dalam tsconfig.json:

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

Kemudian yourPropertyharus dapat diakses di bawah setiap permintaan:

import express from 'express';

const app = express();

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

14

Tidak ada solusi yang ditawarkan yang berhasil untuk saya. Saya akhirnya hanya memperluas antarmuka Permintaan:

import {Request} from 'express';

export interface RequestCustom extends Request
{
    property: string;
}

Kemudian untuk menggunakannya:

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

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

Sunting : Versi terbaru dari TypeScript mengeluh tentang ini. Sebaliknya, saya harus melakukan:

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

1
itu akan berhasil, tetapi cukup bertele-tele jika Anda memiliki 100 fungsi middleware, amirite
Alexander Mills

1
@ user2473015 Ya, versi Typecript terbaru memecahkan ini. Lihat jawaban terbaru saya.
Tom Mettam

8

Di TypeScript, antarmuka terbuka. Itu berarti Anda dapat menambahkan properti ke dalamnya dari mana saja hanya dengan mendefinisikannya kembali.

Mempertimbangkan bahwa Anda menggunakan file express.d.ts ini , Anda harus dapat mendefinisikan ulang antarmuka Request untuk menambahkan field ekstra.

interface Request {
  property: string;
}

Kemudian di fungsi middleware Anda, parameter req harus memiliki properti ini juga. Anda harus dapat menggunakannya tanpa ada perubahan pada kode Anda.


1
Bagaimana Anda "membagikan" informasi itu ke seluruh kode Anda? Jika saya mendefinisikan properti di Request, katakan Request.user = {};di app.tshow does userController.tsknow about it?
Nepoxx

2
@Nepoxx jika Anda mendefinisikan ulang antarmuka, kompilator akan menggabungkan properti dan membuatnya terlihat di mana-mana, itulah alasannya. Idealnya Anda akan melakukan redefinisi dalam file .d.ts. :)
toskv

Itu tampaknya berhasil, namun jika saya menggunakan tipe express.Handler(alih-alih menentukan secara manual (req: express.Request, res: express.Response, next: express.NextFunction) => any)), tampaknya tidak mengacu pada hal yang sama Requestkarena mengeluh bahwa properti saya tidak ada.
Nepoxx

Saya tidak mengharapkannya, kecuali express.Handler memperluas antarmuka Request. melakukannya?
toskv

2
Saya bisa membuatnya bekerja jika saya menggunakan declare module "express"tetapi tidak jika saya menggunakan declare namespace Express. Saya lebih suka menggunakan sintaks namespace tetapi itu tidak berhasil untuk saya.
WillyC

5

Meskipun ini adalah pertanyaan yang sangat lama, saya menemukan masalah ini belakangan ini. Jawaban yang diterima berfungsi dengan baik tetapi saya perlu menambahkan antarmuka khusus ke Request- antarmuka yang telah saya gunakan dalam kode saya dan tidak berfungsi dengan baik dengan yang diterima menjawab. Logikanya, saya mencoba ini:

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

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

Tapi itu tidak berhasil karena Typecript memperlakukan .d.tsfile sebagai impor global dan ketika ada impor di dalamnya, file tersebut diperlakukan sebagai modul normal. Itulah mengapa kode di atas tidak berfungsi pada pengaturan skrip jenis standar.

Inilah yang akhirnya saya lakukan

// typings/common.d.ts

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

export interface ITenant {
    ...
}

Ini berfungsi untuk file utama saya, tetapi tidak di file atau pengontrol perutean saya, saya tidak mendapatkan linting, tetapi ketika saya mencoba untuk mengkompilasi dikatakan "Properti 'pengguna' tidak ada pada jenis 'Permintaan'." (Saya menggunakan pengguna alih-alih penyewa), tetapi jika saya menambahkan // @ ts-abaikan di atasnya, maka itu berhasil (meskipun itu cara konyol untuk memperbaikinya tentu saja. Apakah Anda punya pemikiran tentang mengapa itu mungkin tidak terjadi? bekerja untuk file saya yang lain?
Logan

Itu adalah hal yang sangat aneh @Logan. Dapatkah Anda membagikan Anda .d.ts, tsconfig.jsondan contoh penggunaan? Plus, versi skrip apa yang Anda gunakan karena pengimporan dalam modul global ini hanya didukung mulai dari TS 2.9? Itu bisa membantu lebih baik.
16kb

Saya telah mengunggah data di sini, pastebin.com/0npmR1Zr Saya tidak yakin mengapa penyorotan semuanya kacau meskipun Ini dari file utama prnt.sc/n6xsyl Ini dari file lain prnt.sc/n6xtp0 Jelas beberapa bagian dari ia memahami apa yang sedang terjadi, tetapi kompilator tidak. Saya menggunakan naskah ketikan versi 3.2.2
Logan

1
Ternyata, ... "include": [ "src/**/*" ] ...Berfungsi untuk saya tetapi "include": ["./src/", "./src/Types/*.d.ts"],tidak. Saya belum mencoba memahami hal ini secara mendalam
16kb

Mengimpor antarmuka dengan menggunakan impor dinamis bekerja untuk saya. Terima kasih
Roman Mahotskyi

3

Mungkin masalah ini telah terjawab, tetapi saya ingin berbagi sedikit, sekarang terkadang antarmuka seperti jawaban lain bisa menjadi sedikit terlalu membatasi, tetapi kami sebenarnya dapat mempertahankan properti yang diperlukan dan kemudian menambahkan properti tambahan untuk ditambahkan dengan membuat kunci dengan tipe stringdengan tipe nilaiany

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

Jadi sekarang, kita juga bisa menambahkan properti tambahan yang kita inginkan ke objek ini.


3

Semua tanggapan ini tampaknya salah atau ketinggalan jaman dalam beberapa hal.

Ini berhasil untuk saya pada Mei 2020:

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

import * as express from "express"

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

di tsconfig.json, tambahkan / gabungkan properti seperti itu:

"typeRoots": [ "@types" ]

Bersulang.


Bekerja dengan Webpack + Docker, impor * bisa diganti dengan ekspor {};
Dooomel

2

Jika Anda mencari solusi yang berfungsi dengan express4, ini dia:

@ types / express / index.d.ts: -------- harus /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",
    ]
  }
}

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


1

Salah satu solusi yang mungkin adalah menggunakan "pengecoran ganda ke salah satu"

1- Tentukan antarmuka dengan properti Anda

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

2- pemeran ganda

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

Keuntungan dari pengecoran ganda adalah:

  • pengetikan tersedia
  • ia tidak mencemari definisi yang ada tetapi memperluasnya, menghindari kebingungan
  • karena casting eksplisit, ia mengkompilasi denda dengan -noImplicitanybendera

Alternatifnya, ada rute cepat (tanpa tipe):

 req['myProperty'] = setProperty()

(jangan mengedit file definisi yang ada dengan properti Anda sendiri - ini tidak dapat dipertahankan. Jika definisi salah, buka pull request)

EDIT

Lihat komentar di bawah, casting sederhana bekerja dalam kasus ini req as MyRequest


@akshay Dalam hal ini, ya, karena MyRequestmemperluas http.IncomingMessage. Bukan itu masalahnya, casting ganda melalui anyakan menjadi satu-satunya alternatif
Bruno Grieder

Anda disarankan untuk mentransmisikan ke tidak diketahui, bukan ke mana pun.
dev

0

Jawaban ini akan bermanfaat bagi mereka yang mengandalkan paket npm ts-node .

Saya juga bergumul dengan perhatian yang sama dalam menyampaikan permintaan objek , saya mengikuti banyak jawaban di stack-overflow & diakhiri dengan mengikuti strategi yang disebutkan di bawah ini.

Saya menyatakan pengetikan diperpanjang untuk express di direktori berikut.${PROJECT_ROOT}/api/@types/express/index.d.ts

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

kemudian memperbarui saya tsconfig.jsonke sesuatu seperti ini.

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

bahkan setelah melakukan langkah-langkah di atas, studio visual berhenti mengeluh, tetapi sayangnya, ts-nodecompiler masih digunakan untuk membuang.

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

Rupanya, ts-nodetidak dapat menemukan definisi tipe yang diperluas untuk permintaan objek .

Akhirnya setelah menghabiskan berjam-jam, karena saya tahu VS Code tidak mengeluh & dapat menemukan definisi pengetikan, menyiratkan ada yang salah dengan ts-node complier.

Memperbarui mulai scriptdalam package.jsonmemperbaikinya untuk saya.

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

yang --filesargumen memainkan peran kunci di sini menemukan menentukan definisi jenis kustom.

Untuk informasi lebih lanjut, kunjungi: https://github.com/TypeStrong/ts-node#help-my-types-are-missing


0

Untuk membantu siapa saja yang hanya mencari sesuatu yang lain untuk dicoba di sini, itulah yang berhasil bagi saya pada akhir Mei 2020 ketika mencoba untuk memperpanjang Permintaan ExpressJS. Saya harus mencoba lebih dari selusin hal sebelum ini bekerja:

  • Balik urutan yang direkomendasikan semua orang di "typeRoots" tsconfig.json Anda (dan jangan lupa untuk menghapus jalur src jika Anda memiliki pengaturan rootDir di tsconfig seperti "./src"). Contoh:
"typeRoots": [
      "./node_modules/@types",
      "./your-custom-types-dir"
]
  • Contoh ekstensi khusus ('./your-custom-types-dir/express/index.d.ts "). Saya harus menggunakan impor sebaris dan ekspor default untuk menggunakan kelas sebagai tipe dalam pengalaman saya sehingga itu juga ditampilkan:
declare global {
  namespace Express {
    interface Request {
      customBasicProperty: string,
      customClassProperty: import("../path/to/CustomClass").default;
    }
  }
}
  • Perbarui file nodemon.json Anda untuk menambahkan perintah "--files" ke ts-node, contoh:
{
  "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

Mungkin sudah cukup terlambat untuk jawaban ini, tapi begini cara saya menyelesaikannya:

  1. Pastikan Anda memiliki sumber tipe yang disertakan dalam tsconfigfile Anda (ini bisa menjadi utas yang sama sekali baru)
  2. Di dalam direktori types Anda, tambahkan direktori baru dan beri nama sebagai paket yang ingin Anda perluas atau buat tipe. Dalam kasus khusus ini, Anda akan membuat direktori dengan namaexpress
  3. Di dalam expressdirektori buat file dan beri nama index.d.ts(HARUS PERSIS SEPERTI ITU)
  4. Terakhir untuk membuat extension dari type-type tersebut anda hanya perlu meletakkan kode seperti berikut:
declare module 'express' {
    export interface Request {
        property?: string;
    }
}

-2

mengapa kita perlu melakukan begitu banyak kerumitan seperti dalam jawaban yang diterima di atas, ketika kita bisa lolos dengan melakukan ini saja

alih-alih melampirkan properti kami ke permintaan , kami dapat melampirkannya ke header permintaan

   req.headers[property] = "hello"
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.