Typescript kullanarak Express Request nesnesini genişletin


128

Typecript kullanarak bir ara yazılımdan istek nesnesini ifade etmek için bir özellik eklemeye çalışıyorum. Ancak nesneye nasıl ekstra özellikler ekleyeceğimi çözemiyorum. Mümkünse parantez gösterimi kullanmamayı tercih ederim.

Buna benzer bir şey yazmama izin verecek bir çözüm arıyorum (mümkünse):

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

express.d.ts dosyasının istediğiniz alanlarla sağladığı istek arayüzünü genişletebilmelisiniz.
toskv

Yanıtlar:


147

Özel bir tanım oluşturmak ve Typescript'te Declaration Merging adlı bir özellik kullanmak istiyorsunuz . Bu yaygın olarak kullanılır, örneğin method-override.

Bir dosya oluşturun custom.d.tsve bunu eklemeyi unutmayın tsconfig.json'ın files-bölümü varsa. İçerikler aşağıdaki gibi görünebilir:

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

Bu, kodunuzun herhangi bir noktasında şuna benzer bir şey kullanmanıza olanak tanır:

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
Bunu az önce yaptım, ancak custom.d.ts dosyamı tsconfig.json dosyamdaki dosyalar bölümüne eklemeden çalıştırdım, ancak yine de çalışıyor. Bu beklenen davranış mı?
Chaim Friedman

1
@ChaimFriedman Evet. Bu filesbölüm, TypeScript tarafından dahil edilen dosya kümesini kısıtlar. filesVeya belirtmezseniz includetümü *.d.tsvarsayılan olarak dahil edilir, bu nedenle özel yazılarınızı buraya eklemenize gerek yoktur.
interphx

9
Benim için Property 'tenantçalışmıyor : 'İstek' türünde varolmuyorum `` Açıkça dahil edersem tsconfig.jsonveya eklemesem bir fark yaratmaz . GÜNCELLEME ile declare global@basarat pointet olarak görev yaptığı answear eserlerinde dışarı ama yapmak zorunda import {Request} from 'express'ilki.
Lion

5
FWIW, bu cevap artık geçersiz . RequestJCM'nin cevabı, expressjs'de nesneyi büyütmenin doğru yolu (en az 4.x)
Eric Liprandi

3
Gelecekteki aramalar için - kutunun dışında işe yarayan iyi bir örnek buldum: github.com/3mard/ts-node-example
jd291

79

İçindeki yorumlarınindex.d.ts önerdiği gibi , Expressherhangi bir yeni üyeyi genel ad alanına bildirmeniz yeterlidir . Misal:

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

Tam Örnek:

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

Küresel ad alanlarının genişletilmesi, GitBook'umda daha fazla ele alınmaktadır .


Bildiride neden global'e ihtiyaç var? Orada değilse ne olur?
Jason Kuhrt

Bu, arabirimlerle çalışır, ancak herhangi birinin türleri birleştirmesi gerektiğinde, türlerin "kapalı" olduğunu ve birleştirilemeyeceğini unutmayın: github.com/Microsoft/TypeScript/issues/…
Peter W,

Bay @başarat sana biraz bira borçluyum.
marcellsimon

Ayrıca tsconfig.json'a eklemem gerekiyordu: {"compilerOptions": {"typeRoots": ["./src/typings/", "./node_modules/@types"]}, "files": ["./ src / typings / express / index.d.ts "]}
marcellsimon

Yukarıdaki çözümlerin hiçbiri işe yaramadı .. ama bu ilk çalıştırmada işi yaptı .. çok teşekkürler .. !!
Ritesh

55

Express'in daha yeni sürümleri için, express-serve-static-coremodülü genişletmeniz gerekir .

Bu gereklidir, çünkü artık Express nesnesi oradan geliyor: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/8fb0e959c2c7529b5fa4793a44b41b797ae671b9/types/express/index.d.ts#L19

Temel olarak aşağıdakileri kullanın:

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

1
Bu benim için işe yaradı, ancak eski 'express'modülü genişletmek işe yaramadı. Teşekkür ederim!
Ben Kreeger

4
Bununla uğraşıyordum, bunun işe yaraması için modülü de içe aktarmak zorunda kaldım:import {Express} from "express-serve-static-core";
andre_b

1
@andre_b İpucu için teşekkürler. Import deyiminin dosyayı bir modüle dönüştürdüğünü düşünüyorum ve bu gerekli olan kısım. export {}Hangisinin de çalıştığını kullanmaya geçtim .
Danyal Aytekin

2
Emin bu kod mi gider dosyayı Make değil denilen express.d.tsaksi derleyici hataları sonuçlanan ekspres typings için bu birleştirmeye çalışacağız.
Tom Spencer

3
Türlerinizin typeRoots'ta birinci olması gerektiğinden emin olun! types / express / index.d.ts ve tsconfig => "typeRoots": ["./src/types", "./node_modules/@types"]
kaya

31

Kabul edilen cevap (diğerleri gibi) benim için işe yaramıyor ama

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

yaptı. Umarım bu birine yardımcı olur.


2
Benzer yöntem, "Modül Büyütme" altındaki ts belgelerinde açıklanmaktadır . *.d.tsDosyaları kullanmak istemiyorsanız ve yalnızca türlerinizi normal *.tsdosyalarda saklamak istiyorsanız harikadır .
im.pankratov

3
bu benim için de işe yarayan tek şey, diğer tüm yanıtların .d.ts dosyalarında olması gerekiyor gibi görünüyor
parlamento

custom-declarations.d.tsDosyamı TypeScript'in proje köküne koymam şartıyla bu benim için de işe yarıyor.
focorner

Orijinal türü korumak için genişlettim: import { Request as IRequest } from 'express/index';ve interface Request extends IRequest. Ayrıca typeRoot eklemek zorunda
Ben Creasy

17

8 kadar cevap denedikten ve başarılı olamadıktan sonra. Sonunda jd291'in 3mards deposunu işaret eden yorumu ile çalıştırmayı başardım .

Adlı tabanda bir dosya oluşturun types/express/index.d.ts. Ve içine şunu yazın:

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

ve tsconfig.jsonşununla birlikte ekleyin :

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

Daha sonra yourPropertyher istek altında erişilebilir olmalıdır:

import express from 'express';

const app = express();

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

14

Sunulan çözümlerin hiçbiri benim için işe yaramadı. Sonunda İstek arayüzünü genişlettim:

import {Request} from 'express';

export interface RequestCustom extends Request
{
    property: string;
}

Sonra kullanmak için:

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

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

Düzenleme : TypeScript'in son sürümleri bundan şikayetçi. Bunun yerine yapmak zorundaydım:

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

1
bu işe yarayacak, ancak 100'lerce ara yazılım işlevine sahipseniz oldukça ayrıntılı, amirite
Alexander Mills

1
@ user2473015 Evet, Typescript'in son sürümleri bunu bozdu. Güncellenen cevabıma bakın.
Tom Mettam

8

TypeScript'te arayüzler açık uçludur. Bu, sadece yeniden tanımlayarak onlara her yerden özellikler ekleyebileceğiniz anlamına gelir.

Bu express.d.ts dosyasını kullandığınızı göz önünde bulundurarak , ekstra alan eklemek için İstek arayüzünü yeniden tanımlayabilmelisiniz .

interface Request {
  property: string;
}

Daha sonra ara yazılım işlevinizde, req parametresi de bu özelliğe sahip olmalıdır. Kodunuzda herhangi bir değişiklik yapmadan kullanabilmelisiniz.


1
Bu bilgileri kodunuz boyunca nasıl "paylaşırsınız"? Ben Request bir özelliği tanımlarsanız, demek Request.user = {};de app.tsnasıl yok userController.tshaberi?
Nepoxx

2
@Nepoxx Bir arayüzü yeniden tanımlarsanız, derleyici özellikleri birleştirir ve her yerde görünür hale getirir, bu yüzden. İdeal olarak, yeniden tanımlamayı bir .d.ts dosyasında yaparsınız. :)
toskv

Bu işe yarıyor gibi görünüyor, ancak türü express.Handlerkullanırsam (manuel olarak belirtmek yerine (req: express.Request, res: express.Response, next: express.NextFunction) => any)), mülkümün mevcut olmadığından Requestşikayet ettiği gibi aynı anlama gelmiyor gibi görünüyor .
Nepoxx

Express.Handler, İstek arayüzünü genişletmedikçe, bunu beklemem. yapar?
toskv

2
Kullanırsam çalıştırabilirim declare module "express"ama kullanmazsam değil declare namespace Express. Ad alanı sözdizimini kullanmayı tercih ederim ama bu benim için işe yaramıyor.
WillyC

5

Bu çok eski bir soru olsa da, son zamanlarda bu soruna rastladım.Kabul edilen cevap iyi çalışıyor ancak kodumda kullandığım ve kabul edilenle Requestçok iyi çalışmayan bir arayüze özel bir arayüz eklemem gerekiyordu. Cevap. Mantıksal olarak şunu denedim:

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

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

Ancak bu işe yaramadı çünkü Typescript .d.tsdosyaları genel içe aktarmalar olarak ele alıyor ve içlerinde içe aktarmalar olduğunda bunlar normal modüller olarak değerlendiriliyor. Yukarıdaki kodun standart bir typcript ayarında çalışmamasının nedeni budur.

İşte sonunda yaptığım şey

// typings/common.d.ts

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

export interface ITenant {
    ...
}

Bu benim ana dosyam için çalışıyor, ancak yönlendirme dosyalarımda veya denetleyicilerimde değil, hiçbir şey almıyorum, ancak derlemeye çalıştığımda "Özellik 'kullanıcısı' 'İstek' türünde mevcut değil diyor. (Kiracı yerine kullanıcı kullanıyorum), ancak bunların üstüne // @ ts-ignore eklersem işe yarıyor (gerçi bu elbette düzeltmenin aptalca bir yolu. Neden olmadığına dair herhangi bir fikriniz var mı?) benim diğer dosyalar için çalışan?
Logan

Bu çok garip bir şey @Logan. Eğer paylaşır .d.ts, tsconfig.jsonve kullanım örneği? Ayrıca, genel modüllerdeki bu içe aktarma işlemi yalnızca TS 2.9'dan başlayarak desteklendiğinden, hangi tür komut dosyası sürümünü kullanıyorsunuz? Bu daha iyi yardımcı olabilir.
2019

Verileri buraya yükledim, pastebin.com/0npmR1Zr Vurgulamanın neden karışık olduğundan emin değilim Bu ana dosyadan prnt.sc/n6xsyl Bu başka bir dosyadan prnt.sc/n6xtp0 Açıkçası neler olup bittiğini anlar, ancak derleyici anlamaz. Typcript 3.2.2 sürümünü kullanıyorum
Logan,

1
Şaşırtıcı bir şekilde, ... "include": [ "src/**/*" ] ...benim için çalışıyor ama "include": ["./src/", "./src/Types/*.d.ts"],yapmıyor. Henüz bunu anlamaya çalışmakla
ilgilenmedim

Dinamik içe aktarım kullanarak arayüzü içe aktarmak benim için çalışıyor. Teşekkürler
Roman Mahotskyi

3

Belki bu konu cevaplanmıştır, ancak biraz paylaşmak istiyorum, şimdi bazen diğer cevaplar gibi bir arayüz biraz fazla kısıtlayıcı olabilir, ancak aslında gerekli özellikleri koruyabilir ve sonra eklenecek diğer özellikleri ekleyerek bir stringdeğer türünde bir türe sahip anahtarany

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

Şimdi, bu nesneye istediğimiz herhangi bir ek özelliği de ekleyebiliriz.


3

Tüm bu yanıtlar bir şekilde yanlış veya modası geçmiş görünüyor.

Bu benim için Mayıs 2020'de çalıştı:

içinde ${PROJECT_ROOT}/@types/express/index.d.ts:

import * as express from "express"

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

içinde tsconfig.json, özelliği şu şekilde ekleyin / birleştirin:

"typeRoots": [ "@types" ]

Şerefe.


Webpack + Docker ile çalışır, içe aktarma *, dışa aktarma {} ile değiştirilebilir;
Dooomel

2

Express4 ile çalışan bir çözüm arıyorsanız, işte burada:

@ types / express / index.d.ts: -------- /index.d.ts olmalıdır

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

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


1

Olası bir çözüm, "herhangi birine çift yayınlama" kullanmaktır.

1- Mülkünüzle bir arayüz tanımlayın

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

2- çift döküm

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

Çift dökümün avantajları şunlardır:

  • tipler mevcuttur
  • mevcut tanımları kirletmez, ancak karışıklığı önleyerek genişletir
  • atama açık olduğundan, cezaları -noImplicitanybayrakla derler

Alternatif olarak, hızlı (türlenmemiş) rota vardır:

 req['myProperty'] = setProperty()

(kendi özelliklerinizle mevcut tanım dosyalarını düzenlemeyin - bu sürdürülemez. Tanımlar yanlışsa, bir çekme isteği açın)

DÜZENLE

Aşağıdaki yoruma bakın, bu durumda basit döküm çalışır req as MyRequest


Bu durumda @akshay, evet, çünkü MyRequestuzanır http.IncomingMessage. Durum böyle değildi, anytek alternatif yolla çift döküm olacaktı
Bruno Grieder

Herhangi biri yerine bilinmeyene çevirmeniz önerilir.
dev

0

Bu cevap npm paketine güvenenler için faydalı olacaktır ts-node.

Ben de aynı istek nesnesini genişletme endişesiyle mücadele ediyordum , stack-overflow'da birçok yanıtı takip ettim ve aşağıda belirtilen stratejiyi takip etmekle bitirdim.

Aşağıdaki dizinde express için genişletilmiş yazmayı ilan ettim .${PROJECT_ROOT}/api/@types/express/index.d.ts

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

sonra benim tsconfig.jsongibi bir şeye güncelliyoruz .

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

Yukarıdaki adımları uyguladıktan sonra bile, görsel stüdyo şikayet etmeyi bıraktı ama maalesef ts-nodederleyici atmaya devam ediyordu.

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

Görünüşe göre, istek nesnesi ts-nodeiçin genişletilmiş tür tanımlarını bulamadı .

Sonunda saatler geçirdikten sonra, VS Code'un şikayet etmediğini ve yazım tanımlarını bulabildiğim için ts-nodecomplier ile ilgili bir şeylerin yanlış olduğunu ima etti .

Başlangıç Güncellenmesi scriptiçinde package.jsonbenim için düzelttim.

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

--filesargümanlar burada kilit rol oynayacak özel tip tanımlarını belirleme bulabilirsiniz.

Daha fazla bilgi için lütfen şu adresi ziyaret edin: https://github.com/TypeStrong/ts-node#help-my-types-are-missing


0

Burada denemek için başka bir şey arayan herkese yardım etmek, 2020 yılının Mayıs ayının sonlarında ExpressJS'nin İsteğini genişletmeye çalışırken benim için işe yarayan şeydi. Bunu çalıştırmadan önce bir düzineden fazla şeyi denemem gerekiyordu:

  • Tsconfig.json dosyanızın "typeRoots" bölümünde herkesin önerdiği sırayı ters çevirin (ve tsconfig'de "./src" gibi bir rootDir ayarınız varsa src yolunu bırakmayı unutmayın). Misal:
"typeRoots": [
      "./node_modules/@types",
      "./your-custom-types-dir"
]
  • Özel uzantı örneği ('./your-custom-types-dir/express/index.d.ts "). Deneyimlerimde sınıfları bir tür olarak kullanmak için satır içi içe aktarmayı ve varsayılan dışa aktarmayı kullanmak zorunda kaldım, böylece de gösteriliyor:
declare global {
  namespace Express {
    interface Request {
      customBasicProperty: string,
      customClassProperty: import("../path/to/CustomClass").default;
    }
  }
}
  • Nodemon.json dosyanızı "--files" komutunu ts-node'a eklemek için güncelleyin, örneğin:
{
  "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

Bu cevap için çok geç kalmış olabilir, ama yine de şu şekilde çözdüm:

  1. tsconfigDosyanızda tür kaynağınızın bulunduğundan emin olun (bu tamamen yeni bir konu olabilir)
  2. Türler dizininizin içine yeni bir dizin ekleyin ve genişletmek veya türleri oluşturmak istediğiniz paket olarak adlandırın. Bu özel durumda, adıyla bir dizin oluşturacaksınız.express
  3. expressDizinin içinde bir dosya oluşturun ve adlandırın index.d.ts(TAM OLARAK BUNUN GİBİ OLMALIDIR)
  4. Son olarak, türlerin uzantısını yapmak için aşağıdaki gibi bir kod koymanız yeterlidir:
declare module 'express' {
    export interface Request {
        property?: string;
    }
}

-2

Neden sadece bunu yapmaktan sıyrılabiliyorken, yukarıda kabul edilen cevaplarda olduğu gibi neden bu kadar güçlük çekmemiz gerekiyor?

mülkiyetimizi talebe eklemek yerine, başlık istemek için ekleyebiliriz

   req.headers[property] = "hello"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.