如何以 TypeScript 可以接受的方式获取上传文件的文件名?

How can I get the uploaded file's filename in a way that TypeScript is ok with?

我有一个表格,您可以在其中上传文件。我知道有两种不同的方法来获取文件名,但 TypeScript 都不喜欢它们中的任何一种。编译后的JS文件没有错误,只有TS.

HTML

<form class="image-upload" method="post" enctype="multipart/form-data">
  <input type="file" name="upload-file">
  <input type="submit" value="Upload image">
</form>

TS备选方案1

const form: HTMLFormElement = document.querySelector('.image-upload')!
form.addEventListener('submit', (event) => {
  event.preventDefault()
  const formattedFormData = new FormData(form)
  const data = formattedFormData.get('upload-file')!
  console.log('filename: ', data['name']); // error
})

错误

数据['name'] 上的错误显示为:

Element implicitly has an 'any' type because expression of type '"name"' can't be used to index type 'FormDataEntryValue'. Property 'name' does not exist on type 'FormDataEntryValue'.ts(7053)

为了使这种方法起作用,在我看来我需要将数据(类型 FormDataEntryValue)分配给名称为 属性 的类型。我四处搜索,但我真的找不到任何关于哪些类型具有此 属性.

的信息

TS备选方案2

const form: HTMLFormElement = document.querySelector('.image-upload')!
form.addEventListener('submit', (event) => {
  event.preventDefault()
  console.log('filename: ', document.forms['image-upload']['upload-file'].files[0].name) // error
})

错误

'image-upload' 上的错误为:

Element implicitly has an 'any' type because index expression is not of type 'number'.ts(7015)

老实说,我不知道该怎么办。

tsconfig.json

{
  "compilerOptions": {
    "strict": true
  }
}

const form: HTMLFormElement = document.querySelector('.image-upload')!
form.addEventListener('submit', (event) => {
  event.preventDefault()
  console.log('filename: ', document.forms[0]['upload-file'].files[0].name) // error
})

if (form != null ) {
    const data = new FormData(form);
    for (const pair of data) {
        console.log(pair);
    }
    // OR
    for (const pair of data.entries()) {
        console.log(pair);
    }
}

您可以创建一个 File 变量,您可以在其中放置 document.forms[0]['upload-file'].files[0] :

这样,解释器就会知道您正在尝试访问 class Filename 属性,并且它具有 string 类型,并且不是 any

你必须检查数据是否真的是一个文件。最简单的方法是使用 instanceof.

form.addEventListener('submit', (event) => {
  event.preventDefault()
  const formattedFormData = new FormData(form)
  const data = formattedFormData.get('upload-file')!

  if (data instanceof File) {
    console.log('filename: ', data['name']);
  }
})

Playground link

对于第一个选择:
如果你确定它只会成为一个文件,你可以像 as File.

这样的类型断言
const form: HTMLFormElement = document.querySelector('.image-upload')!
form.addEventListener('submit', (event) => {
    event.preventDefault()
    const formattedFormData = new FormData(form)
    const data = formattedFormData.get('upload-file') as File
    console.log('filename: ', data['name']); // no error
})

如果您不介意它在 JS 代码中结束,您也可以检查 instanceof File(参见 BorisTB 的回答)。

方法 get 返回 FormDataEntryValue(参考:https://developer.mozilla.org/en-US/docs/Web/API/FormData/get
FormDataEntryValuestringFile (参考:https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue
这意味着您可以使用 typeof ... === "string"instanceof File 检查来判断它是哪种类型。

从这里您可以看到 File 的属性:https://developer.mozilla.org/en-US/docs/Web/API/FilenametypelastModified 等)


对于第二种选择:
我会避免在 TS 中这样做,因为用 string 索引 document.forms 会得到结果不确定的类型(HTMLCollection 具有 'item''length''namedItem' 等属性)。
但技术上你可以做到 document.forms['image-upload' as any](显式 any) 只是为了消除警告。

这种方法还有另一个问题:为什么每次都得到 document.forms['image-upload'],而你知道它总是与上面定义的 const form 相同?
你本来可以做 form['upload-file'].files[0].name(不知何故,即使 ...['upload-file'] 结果是 any,TS 也不会抱怨这个。也许一旦它检测到 HTMLFormElement 就很宽容了)

只需在 TS 备选方案 1 的第 5 行添加一个 type assertion:

const data = ... as File;