From bb2673643a8292a565378ffe73b01c37aa7e1194 Mon Sep 17 00:00:00 2001 From: Puneet Arora Date: Wed, 8 Apr 2020 09:29:55 -0700 Subject: [PATCH] [express]: adding generics for query type (#43434) * add-generics-to-query-type: adding generics for query type in express core * add-generics-to-query-type: fixing test case + lint * add-generics-to-query-type: updating the default query type for express * add-generics-to-query-type: fixing failed test cases * add-generics-to-query-type: fixing express-paginate-tests Co-authored-by: Puneet Arora --- types/easy-session/easy-session-tests.ts | 4 ++-- .../express-paginate-tests.ts | 4 ++-- .../express-serve-static-core-tests.ts | 11 +++++++++ types/express-serve-static-core/index.d.ts | 24 +++++++++++-------- types/express/express-tests.ts | 11 +++++++++ types/express/index.d.ts | 2 +- .../ghost-storage-base-tests.ts | 2 +- types/passport-jwt/passport-jwt-tests.ts | 2 +- 8 files changed, 43 insertions(+), 17 deletions(-) diff --git a/types/easy-session/easy-session-tests.ts b/types/easy-session/easy-session-tests.ts index d707e89230..b4d3a3490f 100644 --- a/types/easy-session/easy-session-tests.ts +++ b/types/easy-session/easy-session-tests.ts @@ -49,7 +49,7 @@ app.get('/hasrole', function (req, res, next) { }); app.post('/setrole', function (req, res, next) { - req.session.setRole(req.query.role); + req.session.setRole(req.query.role as string); res.send(200); }); @@ -59,4 +59,4 @@ app.get('/getrole', function (req, res, next) { app.use(easySession.isLoggedIn()); app.use(easySession.isFresh()); -app.use(easySession.checkRole('user')); \ No newline at end of file +app.use(easySession.checkRole('user')); diff --git a/types/express-paginate/express-paginate-tests.ts b/types/express-paginate/express-paginate-tests.ts index ddd87ce441..86f1de4b6a 100644 --- a/types/express-paginate/express-paginate-tests.ts +++ b/types/express-paginate/express-paginate-tests.ts @@ -12,7 +12,7 @@ app.get('/users', async (req, res, next) => { return findAndCountAll({limit: req.query.limit, offset: req.skip}) .then(results => { const itemCount = results.count; - const pageCount = Math.ceil(results.count / req.query.limit); + const pageCount = Math.ceil(results.count / parseInt(req.query.limit as string, 10)); res.render('users/all_users', { users: results.rows, pageCount, @@ -20,7 +20,7 @@ app.get('/users', async (req, res, next) => { currentPageHref: paginate.href(req)(false, req.params), // Instead of exposing this to the html template, we'll test this here and pass a static number hasNextPages: paginate.hasNextPages(req)(pageCount), - pages: paginate.getArrayPages(req)(3, pageCount, req.query.page) + pages: paginate.getArrayPages(req)(3, pageCount, parseInt(req.query.page as string, 10)) }); }).catch(next); }); diff --git a/types/express-serve-static-core/express-serve-static-core-tests.ts b/types/express-serve-static-core/express-serve-static-core-tests.ts index ccd70e7a1f..b9eb7087c7 100644 --- a/types/express-serve-static-core/express-serve-static-core-tests.ts +++ b/types/express-serve-static-core/express-serve-static-core-tests.ts @@ -32,6 +32,17 @@ app.get<{ foo: string }>('/:foo', req => { // Params cannot be a custom type that does not conform to constraint app.get<{ foo: number }>('/:foo', () => {}); // $ExpectError +// Query can be a custom type +app.get<{}, any, any, {q: string}>('/:foo', req => { + req.query.q; // $ExpectType string + req.query.a; // $ExpectError +}); + +// Query will be defaulted to Query type +app.get('/:foo', req => { + req.query; // $ExpectType Query +}); + // Default types app.post("/", (req, res) => { req.params[0]; // $ExpectType string diff --git a/types/express-serve-static-core/index.d.ts b/types/express-serve-static-core/index.d.ts index dad20285af..74f11feac3 100644 --- a/types/express-serve-static-core/index.d.ts +++ b/types/express-serve-static-core/index.d.ts @@ -40,26 +40,30 @@ export interface ParamsDictionary { [key: string]: string; } export type ParamsArray = string[]; export type Params = ParamsDictionary | ParamsArray; -export interface RequestHandler

{ +// Return type of qs.parse, the default query parser (https://expressjs.com/en/api.html#app-settings-property). +export interface Query { [key: string]: string | string[] | Query | Query[]; } + +export interface RequestHandler

{ // tslint:disable-next-line callable-types (This is extended from and can't extend from a type alias in ts<2.2 - (req: Request, res: Response, next: NextFunction): any; + (req: Request, res: Response, next: NextFunction): any; } -export type ErrorRequestHandler

= (err: any, req: Request, res: Response, next: NextFunction) => any; +export type ErrorRequestHandler

= + (err: any, req: Request, res: Response, next: NextFunction) => any; export type PathParams = string | RegExp | Array; -export type RequestHandlerParams

- = RequestHandler - | ErrorRequestHandler +export type RequestHandlerParams

+ = RequestHandler + | ErrorRequestHandler | Array | ErrorRequestHandler

>; export interface IRouterMatcher { // tslint:disable-next-line no-unnecessary-generics (This generic is meant to be passed explicitly.) -

(path: PathParams, ...handlers: Array>): T; +

(path: PathParams, ...handlers: Array>): T; // tslint:disable-next-line no-unnecessary-generics (This generic is meant to be passed explicitly.) -

(path: PathParams, ...handlers: Array>): T; +

(path: PathParams, ...handlers: Array>): T; (path: PathParams, subApplication: Application): T; } @@ -209,7 +213,7 @@ export type Errback = (err: Error) => void; * app.get(/user\/(.*)/, (req, res) => res.send(req.params[0])); * app.get('/user/*', (req, res) => res.send(req.params[0])); */ -export interface Request

extends http.IncomingMessage, Express.Request { +export interface Request

extends http.IncomingMessage, Express.Request { /** * Return request header. * @@ -463,7 +467,7 @@ export interface Request

('/:foo', () => {}); // $ExpectError + // Query can be a custom type + router.get('/:foo', (req: express.Request<{}, any, any , {q: string}>) => { + req.query.q; // $ExpectType string + req.query.a; // $ExpectError + }); + + // Query will be defaulted to any + router.get('/:foo', (req: express.Request<{}>) => { + req.query; // $ExpectType Query + }); + // Response will default to any type router.get("/", (req: Request, res: express.Response) => { res.json({}); diff --git a/types/express/index.d.ts b/types/express/index.d.ts index 8c9246c5b3..128d308237 100644 --- a/types/express/index.d.ts +++ b/types/express/index.d.ts @@ -104,7 +104,7 @@ declare namespace e { interface IRouterMatcher extends core.IRouterMatcher { } interface MediaType extends core.MediaType { } interface NextFunction extends core.NextFunction { } - interface Request

extends core.Request

{ } + interface Request

extends core.Request { } interface RequestHandler

extends core.RequestHandler

{ } interface RequestParamHandler extends core.RequestParamHandler { } export interface Response extends core.Response { } diff --git a/types/ghost-storage-base/ghost-storage-base-tests.ts b/types/ghost-storage-base/ghost-storage-base-tests.ts index a263d2a167..2882bf0033 100644 --- a/types/ghost-storage-base/ghost-storage-base-tests.ts +++ b/types/ghost-storage-base/ghost-storage-base-tests.ts @@ -41,5 +41,5 @@ storage.getSanitizedFileName('IMAGE.jpg'); // $ExpectType string storage.exists('tmp/123456.jpg', '/'); // $ExpectType Promise storage.save(image, '/'); // $ExpectType Promise -storage.serve(); // $ExpectType (req: Request, res: Response, next: NextFunction) => void +storage.serve(); // $ExpectType (req: Request, res: Response, next: NextFunction) => void storage.delete('tmp/123456.jpg', '/'); // $ExpectType Promise diff --git a/types/passport-jwt/passport-jwt-tests.ts b/types/passport-jwt/passport-jwt-tests.ts index 79abad70e0..d5e57300b5 100644 --- a/types/passport-jwt/passport-jwt-tests.ts +++ b/types/passport-jwt/passport-jwt-tests.ts @@ -33,7 +33,7 @@ opts.jwtFromRequest = ExtractJwt.fromUrlQueryParameter('param_name'); opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('param_name'); opts.jwtFromRequest = ExtractJwt.fromExtractors([ExtractJwt.fromHeader('x-api-key'), ExtractJwt.fromBodyField('field_name'), ExtractJwt.fromUrlQueryParameter('param_name')]); opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken(); -opts.jwtFromRequest = (req: Request) => { return req.query.token; }; +opts.jwtFromRequest = (req: Request) => { return req.query.token as string; }; opts.secretOrKey = new Buffer('secret'); declare function findUser(condition: {id: string}, callback: (error: any, user :any) => void): void;