Search code examples
javascriptnode.jstypescriptexpressmulter

TypeScript errors using Multer in Express app


import multer, { FileFilterCallback } from "multer";
import sharp from "sharp";
import { NextFunction, Request, Response } from "express";

const multerStorage = multer.memoryStorage();

const multerFilter = (
  req: Request,
  file: Express.Multer.File,
  cb: FileFilterCallback
) => {
  if (file.mimetype.startsWith("image")) {
    cb(null, true);
  } else {
    cb(new Error("Not an image! Please upload only images."), false);
  }
};

const upload = multer({ storage: multerStorage, fileFilter: multerFilter });

export const uploadRecipeImages = upload.array("images", 6);

export const resizeRecipeImages = async (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  if (!req.files?.images) return next();

  req.body.images = [];

  await Promise.all(
    req.files.images.map(async (file, index) => {
      const filename = `recipe-${req.params.id}-${Date.now()}-${
        index + 1
      }.jpeg`;

      await sharp(file.buffer)
        .resize(1024, 1024)
        .toFormat("jpeg")
        .jpeg({ quality: 90 })
        .toFile(`public/img/recipes/${filename}`);

      req.body.images.push(filename);
    })
  );

  next();
};

I'm using multer package in an express app. I get two TypeScript errors:

  1. Argument of type 'Error' is not assignable to parameter of type 'null'.ts(2345) in cb(new Error("Not an image! Please upload only images."), false);
  2. Property 'images' does not exist on type '{ [fieldname: string]: File[]; } | File[]'. Property 'images' does not exist on type 'File[]'.ts(2339) in if (!req.files?.images) return next();

Solution

  • From the filefilter documentation, the cb can only be used below ways:

    function fileFilter (req, file, cb) {
    
      // The function should call `cb` with a boolean
      // to indicate if the file should be accepted
    
      // To reject this file pass `false`, like so:
      cb(null, false)
    
      // To accept the file pass `true`, like so:
      cb(null, true)
    
      // You can always pass an error if something goes wrong:
      cb(new Error('I don\'t have a clue!'))
    
    }
    

    And, we can find the FileFilterCallback interface:

    /**
     * a function to control which files should be uploaded and which should be skipped
     * pass a boolean to indicate if the file should be accepted
     * pass an error if something goes wrong
     */
    interface FileFilterCallback {
        (error: Error): void;
        (error: null, acceptFile: boolean): void;
    }
    

    Your code cb(new Error("Not an image! Please upload only images."), false); does not match any interface. That's why you got the error.

    So you can call the cb function like cb(new Error("Not an image! Please upload only images.")); to match the (error: Error): void; interface.

    .array(fieldname[, maxCount])

    Accept an array of files, all with the name fieldname. Optionally error out if more than maxCount files are uploaded. The array of files will be stored in req.files.

    The images will be stored in req.files.

    So it should be:

    if (!req.files) return next();
    

    package versions:

    "multer": "^1.4.4",
    "@types/multer": "^1.4.7",