typescript - Argumenttyp string | ArrayBuffer kan inte tilldelas parametertypen string
Får ett TS-fel på rad 15, särskilt i e.target.result.
Argumenttyp string | ArrayBuffer kan inte tilldelas parametrartyp string Typen ArrayBuffer kan inte tilldelas typ 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]);
}
}
lade till HTML
<div class="img-container">
<input type="file" id="filetag">
<img src="" class="profImage" id="preview" alt="profilePic">
</div>
Svar #1
(Oberoende av det faktiska svaret, överväg att använda URL.createObjectURL istället för att konvertera File till en data: URI; det kommer att spara några resurser vid onödiga strängkonverteringar.)
Problemet är att typkontroll fungerar lokalt.
let preview = document.getElementById("preview");
let reader = new FileReader();
reader.onload = function(e) {
preview.setAttribute('src', e.target.result);
}
Baserat på den omgivande kontexten vet TypeScript att preview är en DOM-nod, och därför förväntar sig preview.setAttribute två strängargument. Det vet också att e.target är en FileReader, och därför kan dess egenskap result vara antingen en sträng eller en ArrayBuffer. Vilken av dem det är beror på vilken metod som tidigare kallats på FileReader, men denna information är svår att uttrycka i ett typ-system och är ändå inte tillgänglig inom händelsehanteraren. För allt som typkontrollanten vet kan händelsehanteraren ha anropats efter att readAsArrayBuffer har kallats någonstans långt borta på samma FileReader-objekt.
Eftersom du i det här fallet vet bättre än typkontrollern kan du använda en typkontroll för att övertyga den om att värdet faktiskt är en sträng:
reader.onload = function(e) {
preview.setAttribute('src', e.target.result as string);
}
Om du inte vill skräpa ner din kod med typangivelser överallt, överväg att kapsla in din kod i en abstraktion som är lättare att typkontrollera, till exempel så här:
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]));
}
Nu, om du kommer ihåg att använda readFileAsDataURL överallt i din kod istället för att direkt skapa FileReader-objekt, är den enda platsen där en typförsäkran kommer att behövas inuti just den funktionen.
Svar #2
Du kanske kan göra:
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]))
Eller, om du är säker på att det alltid kommer att vara en sträng:
reader.readAsDataURL(input.files[0] as string);
I princip, om det är en sträng, använd den bara, men om det är en ArrayBuffer, konvertera den först till en sträng. Inte säker på om TypeScript kommer att kunna följa det dock.
https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
Annars, gör bara:
preview.setAttribute('src', e.target.result as string);
igen, förutsatt att du är säker på att det faktiskt alltid kommer att vara en sträng
