0

I am moving my app from js to ts and encountered the following problem:

In my app I use a middleware function to validate requests. It checks stuff like whether req.files.image exists, image type and image size

imageRouter.route('/upload').post(validateUploadImageData, uploadImage)

However, when I move to the next function uploadImage, I notice a typescript error 'req.files' is possibly 'null' or 'undefined'.ts(18049).

I understand why it's happening: ts doesn't know anything about my validations in the other function, it just strictly checks the type in the current function. However, is there a workaround this problem other than doing the same checks and type narrowing in every middleware function?

  • You could just tell the compiler what `req.files` is by writing. `const files = req.files as {image: ...}`. This does not introduce any runtime overhead. – MaximilianMairinger Aug 28 '23 at 02:30
  • Or if ts knows the correct type but assumes it could be undefined/null also. You can use the `!` shorthand to tell the ts compiler that you know the value is not undefined. This also doesn't introduce runtime overhead. Example: `req.files!.images`. For completeness’ sake: there also is optional chaining (`req.files?.images`) which does introduce runtime overhead. – MaximilianMairinger Aug 28 '23 at 02:34

1 Answers1

0

If anyone else is looking for a more elegant and type-secure solution to this problem, check out Zod: https://zod.dev It's a schema declaration and validation library that is really easy to use and it closes the execution gap problem between typescript and javascript.

Now my code looks like this:

import { z } from 'zod'

export const ApplyFilterQueryShema = z.object({

    filterName: z.union([
        z.literal('greyscale'),
        z.literal('sepia'),
        z.literal('reflect'),
        z.literal('blur'),
    ]),
    imageName: z.string(),
})


//controllers file
import { ApplyFilterQueryShema } from '../shemas'

export const applyFilter = function (
    req: Request,
    res: Response,
    next: NextFunction
) {
    try {
        const { imageName, filterName } = ApplyFilterQueryShema.parse(req.query)
..........

What is does is basically it validates the data I receive from ApplyFilterQueryShema.parse(req.query). So we don't need to do 100 lines of validation in every function, but at the same time don't compromise data safety