【问题标题】:Argument type string | ArrayBuffer is not assignable to parameter type string参数类型字符串 | ArrayBuffer 不可分配给参数类型字符串
【发布时间】:2021-03-25 20:48:39
【问题描述】:

在第 15 行出现 TS 错误,尤其是在 e.target.result 中。

参数类型字符串 | ArrayBuffer 不可分配给参数类型字符串  Type ArrayBuffer 不可分配给字符串类型

let fileTag = document.getElementById("filetag"),
    preview = document.getElementById("preview");

fileTag.addEventListener("change", function() {
    changeImage(this);
});

function changeImage(input) {
    let reader;

    if (input.files && input.files[0]) {
        reader = new FileReader();

        reader.onload = function(e) {
            preview.setAttribute('src', e.target.result);
        }

        reader.readAsDataURL(input.files[0]);
    }
}

添加了 HTML

<div class="img-container">
<input type="file" id="filetag">
<img src="" class="profImage" id="preview" alt="profilePic">
</div>

【问题讨论】:

  • Typescript 类型系统不足以表达这种类型,因此您必须强制转换e.target.result as string
  • 在 JavaScript 中一切正常,正如您在此处看到的那样 - jsfiddle.net/s7e1uhx4
  • @georg 你能举个例子吗?
  • @cypho 我的编辑器给了我错误,当我在本地主机上运行它时,图像没有显示出来。

标签: typescript filereader


【解决方案1】:

(独立于实际答案,请考虑使用URL.createObjectURL 而不是将File 转换为data: URI;这将为您节省一些用于虚假字符串转换的资源。)


问题是类型检查在本地工作。

let preview = document.getElementById("preview");
let reader = new FileReader();

reader.onload = function(e) {
    preview.setAttribute('src', e.target.result);
}

根据周围的上下文,TypeScript 知道 preview 是一个 DOM 节点,因此 preview.setAttribute 需要两个字符串参数。它还知道e.targetFileReader,因此its property result 可能是字符串或ArrayBuffer。它是哪一个取决于之前在FileReader 上调用的方法,但是此信息很难在类型系统中表达,并且无论如何在事件处理程序中都不可用。就类型检查器所知,事件处理程序可能是在 readAsArrayBuffer 在同一个 FileReader 对象上被调用后调用的。

由于在这种情况下你比类型检查器更了解,你可以使用type assertion 来说服它该值确实是一个字符串:

reader.onload = function(e) {
    preview.setAttribute('src', e.target.result as string);
}

如果您不想在代码中到处乱扔类型断言,请考虑将代码包装在更易于类型检查的抽象中,例如:

function readFileAsDataURL(file): Promise<string> {
    return new Promise((accept, reject) => {
        const reader = new FileReader();
        reader.onload = function (ev) {
            accept(ev.target.result as string);
        };
        /* XXX: rejecting with an event is rather unorthodox */
        reader.onabort = reader.onerror = function (ev) {
            reject(ev);
        }
        reader.readAsDataURL(file);
    });
}

async function changeImage(input) {
    preview.setAttribute('src', await readFileAsDataURL(input.files[0]));
}

现在,如果您记得在代码中的任何地方都使用 readFileAsDataURL 而不是直接构造 FileReaders,那么唯一需要类型断言的地方就是该函数内部。

【讨论】:

    【解决方案2】:

    你也许可以这样做:

    function ab2str(buf: ArrayBuffer): string {
        return String.fromCharCode.apply(null, new Uint16Array(buf));
    }
    // ...
    reader.readAsDataURL(typeof input.files[0] === 'string' ? input.files[0] : ab2str(input.files[0]))
    

    或者,如果你是肯定的,它将永远是一个字符串:

    reader.readAsDataURL(input.files[0] as string);
    

    基本上如果是字符串,直接用就行,但是如果是ArrayBuffer,先转成字符串。不确定打字稿是否能够遵循这一点。

    https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String

    否则,请执行以下操作:

    preview.setAttribute('src', e.target.result as string);
    

    再一次,假设你是肯定的,它实际上总是一个字符串

    【讨论】:

      猜你喜欢
      • 2021-10-21
      • 2021-10-13
      • 1970-01-01
      • 1970-01-01
      • 2021-09-04
      • 1970-01-01
      • 2021-02-03
      • 1970-01-01
      相关资源
      最近更新 更多