I've been researching how to change the return type of an Observable.
I'm using Angular 5.
Here's an example :
public getButterfly(): Observable<Butterfly[]> {
return http.get<Larva[]>('url').pipe(
map( larva => new Butterfly(larva.dna))
);
}
This code cause an ERROR because for the compiler expect the larva object to be a Butterfly because of the return value and throw an error:
"error TS2339: Property 'id' does not exist on type 'Butterfly[]'."
It seems that typescript doesn't allow type changes inside the observable, but if you know a way, i'm all ears.
Thank you for taking an interest in my question.
get(apiURL) returns an Observable . map is an observable operator which calls a function for each item on its input stream and pushes the result of the function to its output stream. In this case each input item is a Response object.
The map() operator's return value is observable that emits the values from the source Observable transformed by the given project function.
Using Map() method in TypeScript Angular, returning array of Observable in a function.
map is part of the so-called transformation operators group because it's used to transform each item received from the source observable. The operator passes each source value through a projection function to get corresponding output value and emits it to an observer.
I think the accepted answer has masked a bigger problem, and perhaps a confusion in your understanding of what map
actually does in rxjs.
In 'normal' Javascript - the map()
function operates directly on an array and runs a function for every item in the array. eg. [1,2,3].map(x => x + 1)
. You immediately get back a full array of the same length as the original - with the transformed items.
In rxjs
it is a completely different function that's part of rxjs and only has the same name as Array's map
(go find the sourcecode if you dare!).
The actual function you pass to the rxjs map()
still takes a single value and returns a single value (so it looks very much like the pure javascript map) but instead of operating on an array it operates on a single value in a stream.
Ok so you already know RXJS is about streams! And you have a stream of larvae right! (I don't know if this is terrifying or adorable!)
Well you actually don't. With http.get
you do have a stream, but it only contains ONE value and that ONE value is the entire response from your http call whatever it may be.
By the look of it you're returning an array of Larva objects from the get
call, but as far as RXJS is concerned this is a stream of ONLY ONE item.
This is your original code (annotated):
public getButterfly(): Observable<Butterfly[]> {
return http.get<Larva[]>('url').pipe(
map( larva => {
// We are now inside the RXJS 'map' function, NOT a Javascript map
// here larva is an ARRAY (assuming your HTTP call actually returns an array)
// So if you execute larva.dna you'll get 'undefined'
// (because dna is not a property on a javascript array!)
// So you will return a butterfly OBJECT, but initialized with 'undefined' DNA. Scary!
return new Butterfly(larva.dna);
})
);
}
So what I think you actually want is this:
public getButterflies() {
return http.get<Larva[]>('url').pipe(
map( larvae => larvae.map(larva => new Butterfly(larva.dna)))
);
}
If that doesn't immediately make sense here's what's happening:
map
function on that array for EACH item and create a butterfly for each. You then return that new butterfly array as a replacement item in the stream.Butterfly[]
By sticking in <Larva, Butterfly>
you're just telling the compiler that's what it is so you don't get compile time errors. You're never changing anything.
PS. Sometimes I like to specify output type, when it's simple - in order to show mistakes inside the 'pipe'. I'll do this if I have several code paths, each of which must return the same. By constraining the output type it reveals more errors.
Tip: Use tap(x => console.log('Message', x)
in the pipe to write to the console what you have at each stage like this:
public getButterflies() {
return http.get<Larva[]>('url').pipe(
tap( x => console.log('Before map', x) ),
map( larvae => larvae.map(larva => new Butterfly(larva.dna)),
tap( x => console.log('After map', x) )
));
}
Why is it called tap? Because pipes have taps! And a (regular water) tap lets you see what's inside a (regular water) pipe :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With