Reputation: 87
Getting a TS error in line 15 particularly in 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]);
}
}
added HTML
<div class="img-container">
<input type="file" id="filetag">
<img src="" class="profImage" id="preview" alt="profilePic">
</div>
Upvotes: 3
Views: 1741
Reputation: 27221
(Independently of the actual answer, consider using URL.createObjectURL
instead of converting the File
to a data:
URI; it will save you some resources on spurious string conversions.)
The problem is that type checking works locally.
let preview = document.getElementById("preview");
let reader = new FileReader();
reader.onload = function(e) {
preview.setAttribute('src', e.target.result);
}
Based on surrounding context, TypeScript knows that preview
is a DOM node, and therefore preview.setAttribute
expects two string arguments. It also knows that e.target
is a FileReader
, and therefore its property result
may be either a string or an ArrayBuffer
. Which one it is depends on the method that was earlier called on the FileReader
, but this information is hard to express in a type system and is not available within the event handler anyway. For all the type checker knows, the event handler may have been invoked after readAsArrayBuffer
was called somewhere far, far away on the same FileReader
object.
Since in this case you know better than the type checker, you can use a type assertion to convince it that the value is indeed a string:
reader.onload = function(e) {
preview.setAttribute('src', e.target.result as string);
}
If you don’t want to litter your code with type assertions everywhere, consider wrapping your code in an abstraction that is easier to type check, for example like this:
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]));
}
Now if you remember to use readFileAsDataURL
everywhere in your code instead of constructing FileReader
s directly, the only place where a type assertion will be required is inside that one function.
Upvotes: 5
Reputation: 64657
You might be able to do:
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]))
Or, if you are positive it will always be a string:
reader.readAsDataURL(input.files[0] as string);
basically if it's a string, just use it, but if it's an ArrayBuffer, first convert it to a string. Not sure if typescript will be able to follow that though.
https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
Otherwise, just do:
preview.setAttribute('src', e.target.result as string);
again, assuming you are positive it will actually always be a string
Upvotes: -1