使用map更改Observable的返回类型

时间:2018-04-19 08:25:12

标签: angular typescript

我一直在研究如何更改Observable的返回类型。

我正在使用Angular 5.

以下是一个例子:

public getButterfly(): Observable<Butterfly[]> {
    return http.get<Larva[]>('url').pipe(
        map( larva => new Butterfly(larva.dna))
    );
}

此代码导致错误,因为编译器期望幼虫对象因为返回值而成为Butterfly,并抛出错误:

“错误TS2339:'蝴蝶[]'类型中不存在属性'id'。”

似乎打字稿不允许在observable中进行类型更改,但如果你知道一种方法,我就是全部的耳朵。

感谢您对我的问题感兴趣。

2 个答案:

答案 0 :(得分:5)

我认为已接受的答案掩盖了更大的问题,也许您对map在rxjs中的实际作用感到困惑。

在“普通” JavaScript中,map()函数直接在数组上操作,并为数组中的每个项目运行一个函数。例如。 [1,2,3].map(x => x + 1)。您立即取回与原始长度相同的完整数组-包含转换后的项目。

rxjs中,它是rxjs的一部分,它是一个完全不同的函数,仅与Array的map具有相同的名称(如果您敢,请查找源代码!)。

您传递给rxjs map()的实际函数仍然采用单个值并返回单个值(因此,它看起来非常像纯JavaScript映射),但是它不是在数组上进行操作,而是在单个上进行操作流中

好的,所以您已经知道RXJS与流有关!而且您有幼虫流! (我不知道这是恐怖还是可爱!)

实际上你不知道。 使用http.get确实有一个流,但是它仅包含一个值,并且该值是http调用的整个响应,无论它是什么

从外观上看,您正在从get调用中返回一个幼虫对象数组,但是就RXJS而言,这是一个唯一的流。

这是您的原始代码(带注释):

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

所以我认为您真正想要的是:

public getButterflies() {
    return http.get<Larva[]>('url').pipe(
        map( larvae => larvae.map(larva => new Butterfly(larva.dna)))
    );
}

如果那没有立即意义,那就是发生了什么:

  • 您从http调用中获得了一个数组(幼虫-您要告诉打字稿键入为幼虫[])
  • 然后,我们在该数组上针对每个项目运行标准的JavaScript map函数,并为每个项目创建一个蝶形。然后,您将该新的蝶形数组作为流中的替换项返回。
  • 记住您有一个流。但这是一个项目的流。每个项目都是一个数组。
  • 注意:不需要指定输出类型,它会自动Butterfly[]

通过坚持使用<Larva, Butterfly>,您只是告诉就是这样的编译器,因此不会出现编译时错误。您永远不会更改任何内容。

PS。有时,我喜欢指定输出类型,这很简单-为了显示“管道”内部的错误。如果我有多个代码路径,则每个路径必须返回相同的代码,我将执行此操作。通过限制输出类型,它揭示了更多的错误。

提示:在管道中使用tap(x => console.log('Message', x)将每个阶段的内容写到控制台,如下所示:

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

为什么叫水龙头?因为管道有水龙头!而且,(普通水)水龙头可以让您看到(普通水)管道内的东西:-)

答案 1 :(得分:3)

map运算符的类型定义如下:

export declare function map<T, R>(
  project: (value: T, index: number
) => R, thisArg?: any): OperatorFunction<T, R>;

如您所见,您可以在map运算符调用中设置泛型类型。在您的示例中,T值为Butterfly,R值为Larva。

public getButterfly(): Observable<Butterfly[]> {
  return http.get<Larva[]>('url').pipe(
    map<Larva, Butterfly>(larva => new Butterfly(larva.dna))
  );
}