4

I am migrating from JS to TS and encountering some issues along the way. I'm using the widely known multer package with a custom storage engine multer-s3 to efficiently stream incoming files directly to AWS.

The custom storage engine, multer-s3, thereby provides its own definition of the "file" property which extends multer's definition. multer-s3's index.d.ts file looks like this (multer-s3's full type definitions can be found here):

declare global {
    namespace Express {
        namespace MulterS3 {
            interface File extends Multer.File {
                bucket: string;
                key: string;
                acl: string;
                contentType: string;
                contentDisposition: null;
                storageClass: string;
                serverSideEncryption: null;
                metadata: any;
                location: string;
                etag: string;
            }
        }
    }
}

Whenever I'm parsing incoming files though, multer uses its own definition of File rather than multer-s3's definition, which causes properties, like location, to be unavailable on the req.file object.

My question now: how can I get multer to work with multer-s3's file definition rather than its own? Prior to migrating to typescript, all of the properties contained in multer-s3's type definitions for "File" were accessible under req.file - unfortunately, typescript doesn't recognize that. So far I'm somehow using a workaround by creating a new property on my request interface of type Express.MulterS3.File:

export default interface IRequest extends express.Request {
  processedFile?: Express.MulterS3.File;
}

And then, inside the parsing middleware, cast down the req.file property to Express.MulterS3.File, which makes the needed properties available:

export const acceptProfilePicUpload = async function (
  req: IRequest,
  res: express.Response,
  next: express.NextFunction
) {
  try {
    await new Promise((resolve, reject) => {
      multerUpload(req, res, (err: any) => {
        if (err) reject(err);
        else resolve(true);
      });
    });

    if (!req.file)
      throw new Error("file missing");

    req.processedFile = req.file as Express.MulterS3.File; // this is how I get TS to user multer-s3's type definitions - is there a better solution?    

    next();
  } catch (error) {
    console.log(error);
    next(error);
  }
};

However, I'm not sure if that's the right way to do it and would kindly like to ask for your help on this issue. My goal is to simply have req.file be of type Express.MulterS3.File rather than Express.Multer.File.

Maybe some of you know where the issue lies or encountered it themselves when working with multer-s3 & multer in typescript. Any help would be greatly appreciated!

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
linus_hologram
  • 1,595
  • 13
  • 38

1 Answers1

0

I fixed this issue merging the types Express.Multer.File and Express.MulterS3.File.

First, you have to overwrite the type Express.Request.file from express. You can do that by using the key typeRoots in your tsconfig.json. The key receives an array of strings, each string has to be a path that specifies a folder which acts like ./node_modules/@types. Using this key, you'll be able to overwrite types from express or any other library.

Here is my typeRoots:

"typeRoots": [
      "./node_modules/@types",
      "./src/@types"
],

Then, create a file with your Express.Request.file declaration, for example, here is my express.d.ts (the file could have any name):

declare namespace Express {
  export interface Request {
    file: Express.Multer.File & Express.MulterS3.File;
  }
}

If you want to change the type of req.file only for Express.MulterS3.File rather than both, you can write:

declare namespace Express {
  export interface Request {
    file: Express.MulterS3.File;
  }
}
  • I wonder if we can put that inside https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/multer-s3/index.d.ts ? I'm no typescript expert, would this be reasonable ? – GabrielBB Feb 04 '22 at 21:51