I ended up wanting something similar to this but without having to pass the type for the file or the filename, so I made my own example based on @Felix Turner's example. I used the content-disposition header for the filename first in case the file is coming back from an API endpoint, but use the last part of the URL path if that header doesn't exist.
function getFilenameFromContentDisposition(res) {
  let filename = null;
  const disposition = res.headers.get("content-disposition");
  if (disposition?.includes("attachment")) {
    const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
    const matches = filenameRegex.exec(disposition);
    if (matches?.[1]) {
      filename = matches[1].replace(/['"]/g, "");
      // Sometimes the filename comes in a URI encoded format so decode it
      filename = decodeURIComponent(filename);
      // Sometimes the filename starts with UTF-8, remove that
      filename = filename.replace(/^UTF-8/i, "").trim();
    }
  }
  return filename;
}
async function getFileFromLink(url) {
  const fileRes = await fetch(url);
  const blob = await fileRes.blob();
  let fileName = getFilenameFromContentDisposition(fileRes);
  if (!fileName) {
    fileName = url.split("/").pop();
  }
  const file = new File([blob], fileName, {
    type: blob.type,
  });
  return file;
}
If you wanted to make this better, I'd use the content-disposition package on npm for the parsing as the formatting of it can get strange.  I'd also probably use the mime package for ensuring that the filename from the URL has a proper file extension based on the returned content-type