Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending existing properties on @types declarations

Tags:

I am trying to extend the Request object of express that is available on express.Request so that it includes some other data inside the session. I have tried creating my own typings file (d.ts) and using the following code:

import * as express from 'express';

declare module 'express' {
    export interface Request {
        session: express.Request['session'] & {
            myOwnData: string;
        }
    }
}

I am greeted with the following error:

'session' is referenced directly or indirectly in its own type annotation.

What is the correct way to implement this?

like image 427
itsundefined Avatar asked Jan 06 '18 16:01

itsundefined


People also ask

How do you extend type Ts?

Use an intersection type to extend a type in TypeScript, e.g. type TypeB = TypeA & {age: number;} . Intersection types are defined using an ampersand & and are used to combine existing object types. You can use the & operator as many times as necessary to construct a type.

Can an interface extend a type TypeScript?

In TypeScript, interfaces can extend each other just like classes. This lets us copy the members of one interface to another and gives us more flexibility in how we use our interfaces. We can reuse common implementations in different places and we can extend them in different ways without repeating code for interfaces.

Should I use type or interface TypeScript?

Interfaces are most recommended for defining new objects or methods or properties of an object where it will receive a specific component. Hence interface works better when using objects and method objects. Therefore it is our choice to choose between types or interface according to the program needs.

What means T in TypeScript?

This article opts to use the term type variables, coinciding with the official Typescript documentation. T stands for Type, and is commonly used as the first type variable name when defining generics. But in reality T can be replaced with any valid name.


1 Answers

So looking at the express-session type declaration, it declares a Session (and modified Request object) under the Express namespace. Using this we can create a type declaration (mySession.dt.s) to augment the default properties, bypassing the limits of declaration merging:

import {Express} from 'express';
import 'express-session';

declare module 'express' {
    export interface Request {
        session: Express.Session & {
            myOwnData: string;
            myOwnData2: number;
            myOwnData3: boolean;
        };
    }
}

Note that it seems that the compiler is somewhat flexible about the imports in this file (like it doesn't seem to care if Express or Request is imported), but being explicit will be most consistent.

We can then import this declaration into our server file:

import express = require('express');
import {Express, Request, Response} from 'express';
import './mySession';
import * as session from 'express-session';

const app: Express = express();
const PORT = process.env.PORT || process.env.NODE_PORT || 3000;

app.use('/endpoint', (req: Request, res: Response) => {
   const a: number = req.session.myOwnData3 + 2; // fails to compile and highlighted by editors
   console.log(req.session.myOwnData); // compiles and provided autocomplete by Webstorm
   // do stuff
   return res.ok();
});

app.use(session(/** do session stuff **/));

log.info(`Start Express Server on ${PORT}`);
app.listen(PORT);

As tested, this structure achieves type-safety on the added properties, and intellisense/autocomplete in both VSCode and WebStorm with both the express-session and added properties.

Unfortunately, as you noted, this won't be applied globally where type inference is utilized, (only where you explicitly import Request). If you want full control over the interface, you could uninstall @types/express-session and just copy & modify the d.ts (and import that). Another possibility is to declare a totally new property and implement, but obviously a lot more work.

like image 73
Greg Rozmarynowycz Avatar answered Sep 19 '22 12:09

Greg Rozmarynowycz