typescript - Argumentin tyyppi string | ArrayBuffer ei ole osoitettavissa parametrin tyypiksi string


Saan TS-virheen rivillä 15 erityisesti kohdassa e.target.result.

Argumenttityyppi string | ArrayBuffer ei ole määriteltävissä parametrityypille string Tyyppi ArrayBuffer ei ole määriteltävissä tyypille 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]);
    }
}

lisätty HTML

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

Vastaus #1

(Riippumatta varsinaisesta vastauksesta, harkitse URL.createObjectURL-menetelmän käyttöä File-tiedoston muuntamisen sijaan data:-URI:ksi; se säästää resursseja turhissa merkkijonon muunnoksissa.)


Ongelmana on, että tyypin tarkistus toimii paikallisesti.

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

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

Perustuen ympäröivään kontekstiin, TypeScript tietää, että preview on DOM-solmu, ja siksi preview.setAttribute odottaa kahta merkkijonoa sisältävää argumenttia. Se myös tietää, että e.target on FileReader, ja siksi sen ominaisuus result voi olla joko merkkijono tai ArrayBuffer. Kumpi se on, riippuu menetelmästä, jota FileReader -oliolle on aiemmin kutsuttu, mutta tämä tieto on vaikea ilmaista tyyppijärjestelmässä eikä se ole kuitenkaan saatavilla tapahtumakäsittelijän sisällä. Kaiken, mitä tyyppitarkistaja tietää, mukaan tapahtumakäsittelijä on saattanut tulla kutsutuksi sen jälkeen, kun readAsArrayBuffer on kutsuttu jossain kaukaisessa paikassa samalle FileReader -oliolle.

Koska tässä tapauksessa tiedät tarkistajaa paremmin, voit käyttää tyyppivarmistusta vakuuttaaksesi sen siitä, että arvo on todellakin merkkijono:

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

Jos et halua täyttää koodiasi tyyppivarmistuksilla joka paikassa, harkitse koodisi kääntämistä abstraktion sisään, jonka tyyppien tarkistaminen on helpompaa, esimerkiksi näin:

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

Nyt, jos muistat käyttää readFileAsDataURL kaikkialla koodissasi sen sijaan, että rakentaisit FileReader-olioita suoraan, ainoa paikka, jossa tyyppivarmistus tarvitaan, on kyseisen yksittäisen funktion sisällä.

Vastaus #2

Saatat pystyä tekemään:

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

Tai, jos olet varma, että se on aina merkkijono:

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

Periaatteessa, jos se on merkkijono, käytä sitä vain, mutta jos se on ArrayBuffer, muunna se ensin merkkijonoksi. En ole varma, pystyykö TypeScript seuraamaan sitä kuitenkaan.

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

Muuten, tee vain:

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

uudelleen, olettaen että olet varma, että se todella on aina merkkijono

Aiheeseen liittyviä artikkeleita