如何将HttpClient返回类型转为可调用的类实例?
实现HttpClient请求结果转类实例的通用操作
你遇到的问题本质是:Angular HttpClient返回的只是类型断言后的普通JS对象,并不是你定义的类的实例,所以类里的方法自然没法调用。要解决这个问题,完全可以封装通用逻辑,把你写的map里的代码变成可复用的单一调用。
下面给两种可行的实现方式:
方式一:自定义RxJS操作符(推荐)
创建一个通用的操作符mapToInstance,接收类的构造函数,自动把请求数据转成对应类的实例:
import { OperatorFunction } from 'rxjs'; import { map } from 'rxjs/operators'; // 通用操作符:将数据流中的对象转为指定类的实例 export function mapToInstance<T, R>(ctor: new () => R): OperatorFunction<T, R> { return map(data => { const instance = new ctor(); Object.assign(instance, data); return instance; }); }
使用的时候直接在pipe里调用:
this.http.get<IProduct>('https://dummyjson.com/products/1') .pipe(mapToInstance(ProductDisplay)) .subscribe(x => { console.log('obj is ', x); // 这里x是真正的ProductDisplay实例,能调用类里的方法 });
如果你的类需要复杂初始化(比如构造函数传参、额外计算属性),可以给操作符加个可选的初始化函数:
export function mapToInstance<T, R>( ctor: new () => R, initFn?: (instance: R, data: T) => void ): OperatorFunction<T, R> { return map(data => { const instance = new ctor(); Object.assign(instance, data); // 执行自定义初始化逻辑 initFn?.(instance, data); return instance; }); }
调用示例:
this.http.get<IProduct>('https://dummyjson.com/products/1') .pipe(mapToInstance(ProductDisplay, (instance, data) => { // 比如给实例添加计算属性 instance.fullTitle = `${data.title} | ${data.brand}`; })) .subscribe(...);
方式二:拓展Observable原型(按需使用)
如果你想实现类似makeNew<Product>()的链式调用,可以给Observable原型拓展方法:
import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; // 先声明类型,避免TS报错 declare module 'rxjs' { interface Observable<T> { makeNew<R>(ctor: new () => R): Observable<R>; } } // 实现拓展方法 Observable.prototype.makeNew = function <T, R>(ctor: new () => R): Observable<R> { return this.pipe(map(data => { const instance = new ctor(); Object.assign(instance, data); return instance; })); };
这样就能像你期望的那样链式调用:
this.http.get<IProduct>('https://dummyjson.com/products/1') .makeNew(ProductDisplay) .subscribe(x => { console.log('obj is ', x); });
注意:直接修改原型虽然方便,但可能和其他库冲突,非必要的话优先用第一种自定义操作符的方式。
内容的提问来源于stack exchange,提问作者Mathieu Landry
相关产品推荐
相关产品推荐

