How do I get this working in TypeScript? Here is the EventIterator library, which seems to make it possible to build a queue to handle the callbacks of the xhr progress event nicely.
import { EventIterator } from 'event-iterator'
export async function* requestAndWaitForWorkToComplete(request: Request) {
for await (const data of callXhr(request)) {
yield data
switch (data.type) {
case 'request-progress':
console.log('progress', data.percentComplete)
break
// ...
}
}
// TODO: do more stuff here.
}
export type RequestBody = FormData | object
export type Request = {
path: string
method: string
body: RequestBody
}
export type RequestProgress = {
type: 'request-progress'
request: Request
percentComplete: number
}
export type RequestComplete = {
type: 'request-complete'
request: Request
}
export type RequestFailure = {
type: 'request-failure'
request: Request
}
export type CallXhr = RequestProgress | RequestComplete | RequestFailure
export async function* callXhr(
request: Request,
): AsyncIterator<CallXhr> {
return new EventIterator<CallXhr>(({ push }) => {
const xhr = new XMLHttpRequest()
let body
if (typeof request.body === 'string') {
body = request.body
xhr.setRequestHeader('content-type', 'application/json')
} else if (request.body instanceof FormData) {
body = request.body
// headers['content-type'] = 'multipart/form-data'
} else {
body = JSON.stringify(body)
xhr.setRequestHeader('content-type', 'application/json')
}
const isAsync = true
xhr.open(request.method, request.path, isAsync)
xhr.upload.addEventListener('progress', (e: ProgressEvent) => {
const percentComplete = (e.loaded / e.total) * 100
push({ type: 'request-progress', percentComplete, request })
})
xhr.addEventListener('load', function () {
if (this.status == 200) {
push({ type: 'request-complete', request })
} else {
push({ type: 'request-failure', request })
}
})
xhr.send(body)
})
}
Right at callXhr(request) I am getting this in TypeScript:
Type 'AsyncIterator<CallXhr, any, undefined>' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.ts(2504)
(alias) callXhr(request: Request): AsyncIterator<CallXhr, any, undefined>
import callXhr
I don't know if I have typed the callXhr function properly, or really what else I need to add to get this to work. Without the type annotation on callXhr I get this on the data.type call in the switch statement:
Property 'type' does not exist on type 'never'.ts(2339)
I would have assumed the EventIterator, which is class EventIterator<T> extends AsyncIterator<T> would have the type figured out automatically.
How do I properly type this in TypeScript? I have this in my tsconfig.json, do I need to change anything so it works cross-browser?
{
"compilerOptions": {
"module": "ESNext",
"target": "ES2020",
"lib": ["es2020", "dom"],
...
}
}
I tried changing "target": "esnext" but it didn't seem to have any effect.
Also, if there is a simpler way to handle the xhr progress callback without using the third-party library, feel free to show how to do that :). But not a requirement.