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

Argument type string | ArrayBuffer is not assignable to parameter type string

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

Argument type string | ArrayBuffer is not assignable to parameter type string  Type ArrayBuffer is not assignable to type string

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>

你或许可以做到:

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);

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

(独立于实际答案,考虑使用 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.target 是一个 FileReader, and therefore 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 而不是直接构造 FileReader,那么唯一需要类型断言的地方就是在该函数内部。