2016-05-22 15 views
19

Sto tentando di aggiungere una proprietà per esprimere l'oggetto richiesta da un middleware utilizzando typescript. Tuttavia non riesco a capire come aggiungere proprietà extra all'oggetto. Preferirei non usare la notazione delle parentesi se possibile.Estendi oggetto Express Request utilizzando Typescript

Sto cercando una soluzione che mi permettesse di scrivere qualcosa di simile a questo (se possibile):

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

si dovrebbe essere in grado di estendere l'interfaccia richiesta che il file express.d.ts fornisce con i campi desiderati . – toskv

risposta

5

a macchina, le interfacce sono aperte finita. Ciò significa che è possibile aggiungere proprietà a loro da qualsiasi luogo solo ridefinendoli.

Considerando che si sta utilizzando il file express.d.ts, è possibile ridefinire l'interfaccia Richiesta per aggiungere il campo aggiuntivo.

interface Request { 
    property: string; 
} 

Poi nella funzione middleware, il parametro req dovrebbe avere questa proprietà pure. Dovresti essere in grado di usarlo senza modifiche al tuo codice.

+0

Come si" condividono "tali informazioni in tutto il codice? Se definisco una proprietà in Request, dica 'Request.user = {};' in 'app.ts' come fa 'userController.ts' a saperlo? – Nepoxx

+1

@Nepoxx se ridefinite un'interfaccia, il compilatore fonderà le proprietà e le renderà visibili ovunque, ecco perché. Idealmente dovresti fare la ridefinizione in un file .d.ts. :) – toskv

+0

Sembra funzionare, tuttavia se uso il tipo 'express.Handler' (invece di specificare manualmente' (req: express.Request, res: express.Response, next: express.NextFunction) => any) ') , non sembra riferirsi alla stessa 'Richiesta' in quanto lamenta che la mia proprietà non esiste. – Nepoxx

3

Una possibile soluzione è quella di utilizzare "doppio casting per qualsiasi"

1- definire un'interfaccia con la vostra proprietà

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

2- doppio fuso

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

I vantaggi del doppio getto sono che:

  • tipaggi è disponibile
  • non inquina definizioni esistenti ma li estende, evitando confusione
  • poiché il getto è esplicito, compila ammende con il -noImplicitany bandiera

In alternativa, c'è il rapido (senza tipo) percorso:

req['myProperty'] = setProperty() 

(non modificare i file di definizione esistenti con le proprie proprietà - questo non è possibile. Se le definizioni sono sbagliate, aprire una richiesta di pull)

EDIT

vedi commento, semplici lavori di fusione in questo caso req as MyRequest

+0

sarebbe semplicemente 'req come MyRequest' ha funzionato ..? – akshay

+0

@akshay In questo caso, sì, poiché 'MyRequest' estende' http.IncomingMessage'. Non era il caso, il doppio casting tramite 'any' sarebbe l'unica alternativa –

2

I miei 2 cent: estendere il tipo e il codice correnti come al solito.

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


function handler(req: IRequest, res: Response, next: Function) { 
    const authId = req.authId 
    // ... 
} 

const router = Router() 
router.get('/', handler) // no compiling problems 


// this is just for ts, will disappear in transpilation 
interface IRequest extends Request { 
    authId: string // example 
} 
27

si desidera creare una definizione personalizzata, e utilizzare una funzione a macchina chiamata Declaration Merging. Questo è comunemente usato, ad es. in method-override.

Creare un file custom.d.ts e assicurarsi di includerlo nella sezione filesfiles01 se presente.I contenuti possono apparire come segue:

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

Questo vi permetterà, in qualsiasi punto nel codice, utilizzare qualcosa di simile:

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

L'ho appena fatto, ma ho funzionato senza aggiungere il mio file custom.d.ts alla sezione file nel mio tsconfig.json, eppure funziona ancora. È questo comportamento previsto? –

+0

@ChaimFriedman Sì. La sezione 'files' limita il set di file inclusi da TypeScript. Se non si specifica 'files' o' include', tutti i '* .d.ts' sono inclusi per impostazione predefinita, quindi non è necessario aggiungere i tipi di battitura personalizzati lì. – interphx

+0

Non funziona per me: ottengo il 'titolare 'Proprietà' non esiste sul tipo 'Richiesta'' Non fa differenza se lo includo esplicitamente in 'tsconfig.json' o no. ** AGGIORNAMENTO ** Con 'declare global' come @basarat pointet out nei suoi lavori di risposta, ma ho dovuto prima importare {Request} da 'express''. – Lion

8

È sufficiente dichiarare al Express namespace globale nuovi membri . Esempio:

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

completa Esempio:

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

Più

estensione namespace globale è coperto qui: https://basarat.gitbooks.io/typescript/docs/types/lib.d.ts.html

+0

Perché è necessario il global nella dichiarazione? Cosa succede se non è lì? –

1

Nessuna delle soluzioni proposte ha funzionato per me. Ho finito per estendere semplicemente l'interfaccia Richiesta:

import {Request} from 'express'; 

export interface RequestCustom extends Request 
{ 
    property: string; 
} 

Poi, per usarlo:

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

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

che funzionerà, ma abbastanza dettagliato se si dispone di centinaia di funzioni middleware, amirite –