express-generator-typescript 2.0.15 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,9 +14,9 @@
14
14
  },
15
15
  "rules": {
16
16
  "max-len": [
17
- "error",
17
+ "warn",
18
18
  {
19
- "code": 100
19
+ "code": 80
20
20
  }
21
21
  ],
22
22
  "comma-dangle": ["warn", "always-multiline"],
@@ -22,14 +22,15 @@ const authPath = ('/api' + paths.basePath),
22
22
 
23
23
  // Test message
24
24
  const msgs = {
25
- goodLogin: `should return a response with a status of ${OK} and a cookie with a jwt if the login
26
- was successful.`,
27
- emailNotFound: `should return a response with a status of ${UNAUTHORIZED} and a json with the
28
- "error ${UnauthorizedError.Msg}" if the email was not found.`,
29
- pwdFailed: `should return a response with a status of ${UNAUTHORIZED} and a json with the error
30
- "${UnauthorizedError.Msg}" if the password failed.`,
31
- fallbackErr: `should return a response with a status of ${BAD_REQUEST} and a json with an error
32
- for all other bad responses.`,
25
+ goodLogin: `should return a response with a status of ${OK} and a cookie
26
+ with a jwt if the login was successful.`,
27
+ emailNotFound: `should return a response with a status of ${UNAUTHORIZED}
28
+ and a json with the "error ${UnauthorizedError.Msg}" if the email was
29
+ not found.`,
30
+ pwdFailed: `should return a response with a status of ${UNAUTHORIZED} and a
31
+ json with the error "${UnauthorizedError.Msg}" if the password failed.`,
32
+ fallbackErr: `should return a response with a status of ${BAD_REQUEST} and
33
+ a json with an error for all other bad responses.`,
33
34
  goodLogout: `should return a response with a status of ${OK}.`,
34
35
  } as const;
35
36
 
@@ -8,8 +8,13 @@ import userRepo from '@repos/user-repo';
8
8
  import User, { IUser } from '@models/user-model';
9
9
 
10
10
  import { p as userPaths } from '@routes/user-router';
11
+ import {
12
+ ParamInvalidError,
13
+ // ParamMissingError,
14
+ UserNotFoundError,
15
+ ValidatorFnError,
16
+ } from '@shared/errors';
11
17
  import loginAgent from '../support/login-agent';
12
- import { ParamMissingError, UserNotFoundError, ValidatorFnError } from '@shared/errors';
13
18
 
14
19
 
15
20
  // **** Variables **** //
@@ -36,31 +41,34 @@ const dummyUserData = {
36
41
 
37
42
  // Test messages
38
43
  const msgs = {
39
- getUsersSuccess: `should return a JSON object with all the users and a status code of
40
- "${OK}" if the request was successful.`,
41
- getUsersBad: `should return a JSON object containing an error message and a status code of
42
- "${BAD_REQUEST}" if the request was unsuccessful.`,
43
- addUserSuccess: `should return a status code of "${CREATED}" if the request was successful.`,
44
- addUserFailedMissingParam: `should return a JSON object with an error message of
45
- "${ParamMissingError.Msg}" and a status code of "${BAD_REQUEST}" if the user param was
46
- missing.`,
47
- addUserFallbackErr: `should return a JSON object with an error message and a status code
48
- of "${BAD_REQUEST}" if the request was unsuccessful.`,
49
- updateSuccess: `should return a status code of "${OK}" if the request was successful.`,
44
+ getUsersSuccess: `should return a JSON object with all the users and a
45
+ status code of "${OK}" if the request was successful.`,
46
+ getUsersBad: `should return a JSON object containing an error message and a
47
+ status code of "${BAD_REQUEST}" if the request was unsuccessful.`,
48
+ addUserSuccess: `should return a status code of "${CREATED}" if the request
49
+ was successful.`,
50
+ addUserFailedMissingParam: `should return a JSON object with an error
51
+ message of "${ValidatorFnError.Msg + 'instanceOf'}" and a status code
52
+ of "${BAD_REQUEST}" if the user param was missing.`,
53
+ addUserFallbackErr: `should return a JSON object with an error message and a
54
+ status code of "${BAD_REQUEST}" if the request was unsuccessful.`,
55
+ updateSuccess: `should return a status code of "${OK}" if the request was
56
+ successful.`,
50
57
  updateParamMissing: `should return a JSON object with an error message of
51
- "${ParamMissingError.Msg}" and a status code of "${BAD_REQUEST}" if the user param was
52
- missing.`,
58
+ "${ValidatorFnError.Msg + 'instanceOf'}" and a status code of
59
+ "${BAD_REQUEST}" if the user param was missing.`,
53
60
  updateUserNotFound: `should return a JSON object with the error message of
54
- ${UserNotFoundError.Msg} and a status code of "${UserNotFoundError.HttpStatus}" if the id
55
- was not found.`,
56
- updateFallbackErr: `should return a JSON object with an error message and a status code of
57
- "${BAD_REQUEST}" if the request was unsuccessful.`,
58
- deleteSuccessful: `should return a status code of "${OK}" if the request was successful.`,
61
+ ${UserNotFoundError.Msg} and a status code of
62
+ "${UserNotFoundError.HttpStatus}" if the id was not found.`,
63
+ updateFallbackErr: `should return a JSON object with an error message and a
64
+ status code of "${BAD_REQUEST}" if the request was unsuccessful.`,
65
+ deleteSuccessful: `should return a status code of "${OK}" if the request was
66
+ successful.`,
59
67
  deleteUserNotFound: `should return a JSON object with the error message of
60
- ${UserNotFoundError.Msg} and a status code of "${UserNotFoundError.HttpStatus}" if the id was
61
- not found.`,
62
- deleteFallbackErr: `should return a JSON object with an error message and a status code of
63
- "${BAD_REQUEST}" if the request was unsuccessful.`,
68
+ ${UserNotFoundError.Msg} and a status code of
69
+ "${UserNotFoundError.HttpStatus}" if the id was not found.`,
70
+ deleteFallbackErr: `should return a JSON object with an error message and a
71
+ status code of "${BAD_REQUEST}" if the request was unsuccessful.`,
64
72
  };
65
73
 
66
74
 
@@ -267,6 +275,17 @@ describe('user-router', () => {
267
275
  });
268
276
  });
269
277
 
278
+ // Delete, not a valid number error
279
+ it(msgs.deleteUserNotFound, (done) => {
280
+ callApi('horse' as unknown as number)
281
+ .end((err: Error, res: Response) => {
282
+ !!err && logger.err(err);
283
+ expect(res.status).toBe(ParamInvalidError.HttpStatus);
284
+ expect(res.body.error).toBe(ParamInvalidError.Msg);
285
+ done();
286
+ });
287
+ });
288
+
270
289
  // Delete user fallback error
271
290
  it(msgs.deleteFallbackErr, (done) => {
272
291
  spyOn(userRepo, 'persists').and.returnValue(Promise.resolve(true));
@@ -1,3 +1,6 @@
1
+ import { TAll } from '@shared/types';
2
+
3
+
1
4
  // **** Variables **** //
2
5
 
3
6
  export enum UserRoles {
@@ -53,8 +56,9 @@ function copy(user: IUser): IUser {
53
56
  /**
54
57
  * See if an object is an instance of User.
55
58
  */
56
- function instanceOf(arg: object): boolean {
59
+ function instanceOf(arg: TAll): boolean {
57
60
  return (
61
+ !!arg &&
58
62
  typeof arg === 'object' &&
59
63
  'id' in arg &&
60
64
  'email' in arg &&
@@ -1,6 +1,7 @@
1
1
  /**
2
- * Pre-start is where we want to place things that must run BEFORE the express server is started.
3
- * This is useful for environment variables, command-line arguments, and cron-jobs.
2
+ * Pre-start is where we want to place things that must run BEFORE the express
3
+ * server is started. This is useful for environment variables, command-line
4
+ * arguments, and cron-jobs.
4
5
  */
5
6
 
6
7
  import path from 'path';
@@ -4,7 +4,7 @@ import StatusCodes from 'http-status-codes';
4
4
  import authService from '@services/auth-service';
5
5
  import envVars from 'src/shared/env-vars';
6
6
  import { IReq, IRes } from '@shared/types';
7
- import validate from './validators';
7
+ import { vld } from './middleware';
8
8
 
9
9
 
10
10
  // **** Types **** //
@@ -17,8 +17,6 @@ interface ILoginReq {
17
17
 
18
18
  // **** Variables **** //
19
19
 
20
- const router = Router();
21
-
22
20
  // Status codes
23
21
  const { OK } = StatusCodes;
24
22
 
@@ -30,30 +28,36 @@ export const p = {
30
28
  } as const;
31
29
 
32
30
 
33
- // **** Routes **** //
31
+ // **** Setup Router **** //
32
+
33
+ const router = Router();
34
+ router.post(p.login, vld('email', 'password'), login);
35
+ router.get(p.logout, logout);
36
+
37
+
38
+ // **** Functions **** //
34
39
 
35
40
  /**
36
41
  * Login a user.
37
42
  */
38
- router.post(p.login, async (req: IReq<ILoginReq>, res: IRes) => {
43
+ async function login(req: IReq<ILoginReq>, res: IRes) {
39
44
  const { email, password } = req.body;
40
- validate(email, password);
41
45
  // Add jwt to cookie
42
- const jwt = await authService.login(email, password);
46
+ const jwt = await authService.getJwt(email, password);
43
47
  const { key, options } = envVars.cookieProps;
44
48
  res.cookie(key, jwt, options);
45
49
  // Return
46
50
  return res.status(OK).end();
47
- });
51
+ }
48
52
 
49
53
  /**
50
54
  * Logout the user.
51
55
  */
52
- router.get(p.logout, (_: IReq, res: IRes) => {
56
+ function logout(_: IReq, res: IRes) {
53
57
  const { key, options } = envVars.cookieProps;
54
58
  res.clearCookie(key, options);
55
59
  return res.status(OK).end();
56
- });
60
+ }
57
61
 
58
62
 
59
63
  // **** Export router **** //
@@ -1,10 +1,12 @@
1
1
  import StatusCodes from 'http-status-codes';
2
- import { Request, Response, NextFunction } from 'express';
2
+ import { Request, Response, NextFunction, RequestHandler } from 'express';
3
3
  import { JwtPayload } from 'jsonwebtoken';
4
4
 
5
- import { IUser, UserRoles } from '@models/user-model';
6
5
  import envVars from 'src/shared/env-vars';
7
6
  import jwtUtil from '@util/jwt-util';
7
+ import { IUser, UserRoles } from '@models/user-model';
8
+ import { ParamInvalidError, ValidatorFnError } from '@shared/errors';
9
+ import { TAll } from '@shared/types';
8
10
 
9
11
 
10
12
  // **** Variables **** //
@@ -15,6 +17,13 @@ const jwtNotPresentErr = 'JWT not present in signed cookie.';
15
17
 
16
18
  // **** Types **** //
17
19
 
20
+ type TValidatorFn = ((arg: TAll) => boolean);
21
+ type TParamArr = {
22
+ 0: string,
23
+ 1?: string | TValidatorFn,
24
+ 2?: 'body' | 'query' | 'params',
25
+ }
26
+
18
27
  export interface ISessionUser extends JwtPayload {
19
28
  id: number;
20
29
  email: string;
@@ -28,7 +37,11 @@ export interface ISessionUser extends JwtPayload {
28
37
  /**
29
38
  * Middleware to verify if user logged in.
30
39
  */
31
- export async function adminMw(req: Request, res: Response, next: NextFunction) {
40
+ export async function adminMw(
41
+ req: Request,
42
+ res: Response,
43
+ next: NextFunction,
44
+ ) {
32
45
  try {
33
46
  // Extract the token
34
47
  const cookieName = envVars.cookieProps.key,
@@ -55,3 +68,95 @@ export async function adminMw(req: Request, res: Response, next: NextFunction) {
55
68
  }
56
69
  }
57
70
 
71
+
72
+
73
+ /**
74
+ * vld stands for 'validate'. This function returns a middleware-function
75
+ * that validates the parameters provided. Each argument can be a string or
76
+ * array. If an array the format is:
77
+ *
78
+ * ['paramName', '(optional) type or validator function (default is string)',
79
+ * '(optional) property on "req" to extract the value from (default is body')]
80
+ *
81
+ * If a string, vld makes sure the param is of type string on req.body. For
82
+ * the array, vld makes sure parameter is of the specified type or that the
83
+ * parameter satifies the validator function. The validator function must
84
+ * return 'true' or 'false'.
85
+ *
86
+ * **NOTE**: For numbers, strings which evalute to (!isNaN('string') => true)
87
+ * are valid for req.query and req.params. But on req.body a number should be
88
+ * 'typeof toCheck === "number"'
89
+ *
90
+ * Sample argument: ['id', 'number'], id is the incoming variable, 'number'
91
+ * is the type, and body is where to extract 'id' from.
92
+ *
93
+ * Example 1: vld('email', ['user', 'object'], ['id', 'number', 'params']) |
94
+ * will check that 'email' is a string in req.body, that user is of type
95
+ * object in req.body, and that 'id' is a boolean in req.params.
96
+ * Example 2: vld('password') | will check that 'password' is a string on
97
+ * req.body.
98
+ * Example 3: vld(['isAdmin', 'boolean']) | will check that 'isAdmin' is a
99
+ * boolean on req.body
100
+ * Example 4: vld(['user', isInstanceOfUser]) | will check that 'user'
101
+ * satifies the 'isInstanceOfUser()' function.
102
+ * Example 5: val(['email']), will check that 'email' is a string in req.body,
103
+ */
104
+ export function vld(...params: Array<string | TParamArr>): RequestHandler {
105
+ return (req: Request, _: Response, next: NextFunction) => {
106
+ for (const param of params) {
107
+ // Check params
108
+ if (typeof param !== 'string' && !(param instanceof Array)) {
109
+ throw Error('vld() arg must be a string or array');
110
+ }
111
+ // If param is a string, make sure param is of type string on req.body
112
+ if (typeof param === 'string') {
113
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
114
+ if (typeof req.body[param] !== 'string') {
115
+ throw new ParamInvalidError();
116
+ }
117
+ continue;
118
+ }
119
+ // Get the value to validate from the express Request object.
120
+ const paramName = param[0];
121
+ const reqObjProp = (param.length >= 3 ? param[2] : 'body');
122
+ // Validate request object property
123
+ if (
124
+ reqObjProp !== 'body' &&
125
+ reqObjProp !== 'params' &&
126
+ reqObjProp !== 'query'
127
+ ) {
128
+ throw Error('param[1] must be "body", "query", or "params"');
129
+ }
130
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
131
+ const toValidate = req[reqObjProp][paramName] as TAll;
132
+ // If type is not present, check it's a string by default
133
+ if (param.length === 1) {
134
+ if (typeof toValidate !== 'string') {
135
+ throw new ParamInvalidError();
136
+ }
137
+ // If a validator function is given, validate it with that
138
+ } else if (typeof param[1] === 'function') {
139
+ const fn = param[1];
140
+ if (!fn(toValidate)) {
141
+ throw new ValidatorFnError(fn.name);
142
+ }
143
+ // If type is a number, need to do isNaN() to account for
144
+ // number-strings (i.e. '1243') in req.query or req.params. But in
145
+ // req.body, numbers should be 'typeof toCheck === "number"'
146
+ } else if (param[1] === 'number') {
147
+ if (reqObjProp === 'query' || reqObjProp === 'params') {
148
+ if (isNaN(toValidate as number)) {
149
+ throw new ParamInvalidError();
150
+ }
151
+ } else if (typeof toValidate !== 'number') {
152
+ throw new ParamInvalidError();
153
+ }
154
+ // Check the param is the provided type
155
+ } else if (typeof toValidate !== param[1]) {
156
+ throw new ParamInvalidError();
157
+ }
158
+ }
159
+ // Call next() if no errors thrown
160
+ next();
161
+ };
162
+ }
@@ -4,14 +4,14 @@ import { Router } from 'express';
4
4
  import userService from '@services/user-service';
5
5
  import User, { IUser } from '@models/user-model';
6
6
  import { IReq, IRes } from '@shared/types';
7
- import validate from './validators';
7
+ import { vld } from './middleware';
8
+
8
9
 
9
10
 
10
11
  // **** Variables **** //
11
12
 
12
13
  // Misc
13
- const router = Router(),
14
- { CREATED, OK } = StatusCodes;
14
+ const { CREATED, OK } = StatusCodes;
15
15
 
16
16
  // Paths
17
17
  export const p = {
@@ -23,48 +23,56 @@ export const p = {
23
23
  } as const;
24
24
 
25
25
 
26
- // **** Routes **** //
26
+ // **** Setup Router **** //
27
+
28
+ // Validators (place shared or really long ones here)
29
+ const validateUser = vld(['user', User.instanceOf]);
30
+
31
+
32
+ // Add routes to express
33
+ const router = Router();
34
+ router.get(p.get, getAll);
35
+ router.post(p.add, validateUser, add);
36
+ router.put(p.update, validateUser, update);
37
+ router.delete(p.delete, vld(['id', 'number', 'params']), _delete);
38
+
39
+
40
+ // **** Functions **** //
27
41
 
28
42
  /**
29
43
  * Get all users.
30
44
  */
31
- router.get(p.get, async (_: IReq, res: IRes) => {
45
+ async function getAll(_: IReq, res: IRes) {
32
46
  const users = await userService.getAll();
33
47
  return res.status(OK).json({ users });
34
- });
48
+ }
35
49
 
36
50
  /**
37
51
  * Add one user.
38
52
  */
39
- router.post(p.add, async (req: IReq<{user: IUser}>, res: IRes) => {
53
+ async function add(req: IReq<{user: IUser}>, res: IRes) {
40
54
  const { user } = req.body;
41
- validate([user, User.instanceOf]);
42
- // Add user
43
55
  await userService.addOne(user);
44
56
  return res.status(CREATED).end();
45
- });
57
+ }
46
58
 
47
59
  /**
48
60
  * Update one user.
49
61
  */
50
- router.put(p.update, async (req: IReq<{user: IUser}>, res: IRes) => {
62
+ async function update(req: IReq<{user: IUser}>, res: IRes) {
51
63
  const { user } = req.body;
52
- validate([user, User.instanceOf]);
53
- // Edit user
54
64
  await userService.updateOne(user);
55
65
  return res.status(OK).end();
56
- });
66
+ }
57
67
 
58
68
  /**
59
69
  * Delete one user.
60
70
  */
61
- router.delete(p.delete, async (req: IReq, res: IRes) => {
62
- const id = Number(req.params.id);
63
- validate([id, 'number']);
64
- // Delete user
71
+ async function _delete(req: IReq, res: IRes) {
72
+ const id = +req.params.id;
65
73
  await userService.delete(id);
66
74
  return res.status(OK).end();
67
- });
75
+ }
68
76
 
69
77
 
70
78
  // **** Export default **** //
@@ -40,11 +40,22 @@ if (envVars.nodeEnv === 'production') {
40
40
  // Add APIs
41
41
  app.use('/api', BaseRouter);
42
42
 
43
- // Error handling
44
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45
- app.use((err: Error | CustomError, req: Request, res: Response, _: NextFunction) => {
43
+ // Setup error handler
44
+ app.use((
45
+ err: Error | CustomError,
46
+ _: Request,
47
+ res: Response,
48
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
49
+ __: NextFunction,
50
+ ) => {
46
51
  logger.err(err, true);
47
- const status = (err instanceof CustomError ? err.HttpStatus : StatusCodes.BAD_REQUEST);
52
+ // Status
53
+ const status = (
54
+ err instanceof CustomError
55
+ ? err.HttpStatus
56
+ : StatusCodes.BAD_REQUEST
57
+ );
58
+ // Return
48
59
  return res.status(status).json({
49
60
  error: err.message,
50
61
  });
@@ -10,7 +10,7 @@ import { UnauthorizedError } from '@shared/errors';
10
10
  /**
11
11
  * Login a user.
12
12
  */
13
- async function login(email: string, password: string): Promise<string> {
13
+ async function getJwt(email: string, password: string): Promise<string> {
14
14
  // Fetch user
15
15
  const user = await userRepo.getOne(email);
16
16
  if (!user) {
@@ -35,5 +35,5 @@ async function login(email: string, password: string): Promise<string> {
35
35
  // **** Export default **** //
36
36
 
37
37
  export default {
38
- login,
38
+ getJwt,
39
39
  } as const;
@@ -11,23 +11,14 @@ export abstract class CustomError extends Error {
11
11
  }
12
12
  }
13
13
 
14
- export class ParamMissingError extends CustomError {
15
-
16
- public static readonly Msg = 'One or more of the required parameters was missing.';
17
- public static readonly HttpStatus = HttpStatusCodes.BAD_REQUEST;
18
-
19
- constructor() {
20
- super(ParamMissingError.Msg, ParamMissingError.HttpStatus);
21
- }
22
- }
23
-
24
14
  export class ParamInvalidError extends CustomError {
25
15
 
26
- public static readonly Msg = 'One or more of the required was missing or invalid.';
16
+ public static readonly Msg = 'One or more of the required was missing ' +
17
+ 'or invalid.';
27
18
  public static readonly HttpStatus = HttpStatusCodes.BAD_REQUEST;
28
19
 
29
20
  constructor() {
30
- super(ParamInvalidError.Msg, ParamMissingError.HttpStatus);
21
+ super(ParamInvalidError.Msg, ParamInvalidError.HttpStatus);
31
22
  }
32
23
  }
33
24
 
@@ -37,13 +28,14 @@ export class ValidatorFnError extends CustomError {
37
28
  public static readonly HttpStatus = HttpStatusCodes.BAD_REQUEST;
38
29
 
39
30
  constructor(fnName: string) {
40
- super(ValidatorFnError.Msg + fnName, ParamMissingError.HttpStatus);
31
+ super(ValidatorFnError.Msg + fnName, ValidatorFnError.HttpStatus);
41
32
  }
42
33
  }
43
34
 
44
35
  export class UserNotFoundError extends CustomError {
45
36
 
46
- public static readonly Msg = 'A user with the given id does not exists in the database.';
37
+ public static readonly Msg = 'A user with the given id does not exists ' +
38
+ 'in the database.';
47
39
  public static readonly HttpStatus = HttpStatusCodes.NOT_FOUND;
48
40
 
49
41
  constructor() {
@@ -4,6 +4,11 @@ import { Query } from 'express-serve-static-core';
4
4
  import { ISessionUser } from '@routes/middleware';
5
5
 
6
6
 
7
+ // **** Misc **** //
8
+
9
+ export type TAll = string | number | boolean | null | object;
10
+
11
+
7
12
  // **** Express **** //
8
13
 
9
14
  export interface IReq<T = void> extends e.Request {
@@ -1,4 +1,4 @@
1
- import jsonwebtoken, { JwtPayload } from 'jsonwebtoken';
1
+ import jsonwebtoken from 'jsonwebtoken';
2
2
  import envVars from '../shared/env-vars';
3
3
 
4
4
 
@@ -31,7 +31,7 @@ function sign(data: string | object | Buffer): Promise<string> {
31
31
  /**
32
32
  * Decrypt JWT and extract client data.
33
33
  */
34
- async function decode<T extends JwtPayload>(jwt: string): Promise<string | undefined | T> {
34
+ async function decode<T>(jwt: string): Promise<string | undefined | T> {
35
35
  return new Promise((res, rej) => {
36
36
  jsonwebtoken.verify(jwt, envVars.jwt.secret, (err, decoded) => {
37
37
  return err ? rej(errors.validation) : res(decoded as T);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-generator-typescript",
3
- "version": "2.0.15",
3
+ "version": "2.1.2",
4
4
  "description": "Generate new Express applications similar to express-generate which but sets it up to use TypeScript instead",
5
5
  "scripts": {
6
6
  "express-generator-typescript": "node bin/cli.js",
@@ -1,60 +0,0 @@
1
- /**
2
- * Put other validator functions here too that may be passed to the validate() function
3
- */
4
-
5
- import { ParamInvalidError, ValidatorFnError } from '@shared/errors';
6
-
7
-
8
- // **** Types **** //
9
-
10
- type TParam = string | number | boolean | null | object;
11
- type TValidatorFn = ((arg: TParam) => boolean);
12
- type TParamFull = TParam | TValidatorFn;
13
-
14
-
15
- // **** Functions **** //
16
-
17
- /**
18
- * Make sure parameter is of the specified type. If the type is not provided then 'string' is
19
- * used as the default type. Instead of a type you can also pass a validator function
20
- * which must return 'true' or 'false'. Return false if invalid.
21
- *
22
- * Example argument: [id, 'number'], id is the incoming variable, 'number' is the type.
23
- *
24
- * Example 1: validate(email, [user, 'object'], [isAdmin, 'boolean'])
25
- * Example 2: validate(password) // will check that password is a string
26
- * Example 3: validate([isAdmin, 'boolean'])
27
- * Example 4: validate([user, isInstanceOfUser])
28
- */
29
- function validate(...params: Array<TParam | TParamFull[]>): void {
30
- for (const param of params) {
31
- // If param is array check provided type
32
- if (param instanceof Array) {
33
- // Validator function provided
34
- if (typeof param[1] === 'function') {
35
- const fn = param[1];
36
- if (!fn(param[0])) {
37
- throw new ValidatorFnError(fn.name);
38
- }
39
- // If type is a number, need to do isNaN, cause type NaN is a 'number'.
40
- // This could be an issue if you do Number(undefined)
41
- } else if (param[1] === 'number') {
42
- if (isNaN(param[0] as number)) {
43
- throw new ParamInvalidError();
44
- }
45
- } else if (typeof param[0] !== param[1]) {
46
- throw new ParamInvalidError();
47
- }
48
- // String is default if type is not provided
49
- } else {
50
- if (typeof param !== 'string') {
51
- throw new ParamInvalidError();
52
- }
53
- }
54
- }
55
- }
56
-
57
-
58
- // **** Export default **** //
59
-
60
- export default validate;