typescript - Argüman tipi string | ArrayBuffer, parametre tipi string ile atanamaz


Özellikle e.target.result kısmında 15. satırda TS hatası alıyorum.

Argüman türü string | ArrayBuffer, parametre türü string'e atanamaz ArrayBuffer türü string türüne atanamaz

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 eklendi

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

Cevap #1

(Gerçek yanıtından bağımsız olarak, File öğesini bir data: URI’sine dönüştürmek yerine URL.createObjectURL kullanmayı düşünün; gereksiz string dönüşümlerinden bazı kaynakları tasarruf etmenizi sağlar.)


Sorun, tür kontrolünün yerel olarak çalışmasıdır.

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

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

Çevresindeki bağlama dayanarak, TypeScript preview'un bir DOM düğümü olduğunu bilir ve bu nedenle preview.setAttribute iki string argüman bekler. Ayrıca e.target'in bir FileReader olduğunu bilir ve bu nedenle result özelliği ya bir string ya da bir ArrayBuffer olabilir. Hangisi olduğu, daha önce FileReader üzerinde hangi yöntemin çağrıldığına bağlıdır, ancak bu bilgi bir tür sistemi içinde ifade etmek zordur ve etkinlik işleyici içinde bu bilgi mevcut değildir. Tür denetleyicisinin bildiği kadarıyla, etkinlik işleyici, aynı FileReader nesnesinde çok uzakta bir yerde readAsArrayBuffer çağrıldıktan sonra da tetiklenmiş olabilir.

Bu durumda, tür denetleyiciden daha iyi bildiğiniz için, değerin gerçekten bir string olduğunu ona inandırmak için bir tür doğrulaması kullanabilirsiniz:

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

Kodunuzu her yerde tür bildirimleriyle doldurmak istemiyorsanız, kodunuzu tür denetlemesi daha kolay olan bir soyutlama içinde sarmayı düşünün, örneğin şöyle:

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

Artık eğer kodunuzda FileReaderları doğrudan oluşturmak yerine her yerde readFileAsDataURL kullanmayı hatırlarsanız, tür bildirimi gerekecek tek yer o fonksiyonun içinde olacaktır.

Cevap #2

Şunu yapabilirsin:

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

Ya da, her zaman bir string olacağından emin isen:

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

Temel olarak eğer bir string ise, sadece kullan; ama eğer bir ArrayBuffer ise, önce onu bir string'e çevir. Yine de TypeScript'in bunu takip edip edemeyeceğinden emin değilim.

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

Aksi takdirde, sadece şunu yap:

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

yine, bunun gerçekten her zaman bir string olacağından emin olduğunuzu varsayarsak

İlgili makaleler