TypeScript类型不兼容:如何让sendRequest支持多类型参数并解决报错?
解决TypeScript联合类型引发的Observable类型不兼容问题
先看你遇到的代码场景(来自《Pro Angular》第8章):
getProducts(): Observable<Product[]> { return this.sendRequest(RequestMethod.Get, "products"); } saveOrder(order: Order) : Observable<Order> { return this.sendRequest(RequestMethod.Post, "orders", order); } private sendRequest(verb: RequestMethod, url: string, body?: Product | Order): Observable<Product[] | Order> { return this.http.request(new Request({ method: verb, url: this.baseUrl + url, body: body })).map(response => response.json()) }
对应的报错信息分别是:
[ts]Type 'Observable<Product[] | Order>' is not assignable to type 'Observable<Product[]>'. Type 'Product[] | Order' is not assignable to type 'Product[]'. Type 'Order' is not assignable to type 'Product[]'. Property 'length' is missing in type 'Order'.
和
[ts]Type 'Observable<Product[] | Order>' is not assignable to type 'Observable<Order>'. Type 'Product[] | Order' is not assignable to type 'Order'. Type 'Product[]' is not assignable to type 'Order'. Property 'id' is missing in type 'Product[]'.
问题根源
sendRequest方法返回了Observable<Product[] | Order>的联合类型,但getProducts需要明确的Observable<Product[]>,saveOrder需要明确的Observable<Order>。TypeScript不允许把“可能是A或B”的类型直接赋值给“必须是A”或“必须是B”的类型,这是类型安全机制的严格体现。
解决方案:用泛型让方法返回指定类型
最优雅的解决方式是给sendRequest添加泛型参数,让它根据调用场景返回我们需要的具体类型,同时保留body参数对Product或Order的支持。
修改后的代码如下:
// 给sendRequest添加泛型T,指定返回的Observable类型 private sendRequest<T>(verb: RequestMethod, url: string, body?: Product | Order): Observable<T> { return this.http.request(new Request({ method: verb, url: this.baseUrl + url, body: body })).map(response => response.json() as T); // 把解析后的JSON断言为T类型 } // 调用时明确指定泛型类型 getProducts(): Observable<Product[]> { return this.sendRequest<Product[]>(RequestMethod.Get, "products"); } saveOrder(order: Order): Observable<Order> { return this.sendRequest<Order>(RequestMethod.Post, "orders", order); }
为什么这样有效?
- 泛型参数
T让我们在调用sendRequest时,可以明确告诉TypeScript这个调用应该返回什么类型的Observable,不再是模糊的联合类型。 response.json() as T是类型断言,在你明确知道API返回格式的情况下,告诉TypeScript“我确定解析后的JSON就是T类型”,这是安全且合理的操作。- body参数依然保留了
Product | Order的类型,支持两种类型的传入,完全满足你的需求。
可选的进阶优化(更严谨的类型约束)
如果你想让body的类型和返回类型更关联,避免传入和当前请求不匹配的body类型,可以给body也添加泛型参数:
private sendRequest<TResponse, TBody = never>( verb: RequestMethod, url: string, body?: TBody ): Observable<TResponse> { return this.http.request(new Request({ method: verb, url: this.baseUrl + url, body: body })).map(response => response.json() as TResponse); } // 调用时同时指定返回类型和body类型 getProducts(): Observable<Product[]> { return this.sendRequest<Product[]>(RequestMethod.Get, "products"); // 无body,不用指定TBody } saveOrder(order: Order): Observable<Order> { return this.sendRequest<Order, Order>(RequestMethod.Post, "orders", order); }
这种方式会让类型检查更严格,比如你不会不小心把Product传入saveOrder的调用中。
内容的提问来源于stack exchange,提问作者Liang
相关产品推荐
相关产品推荐

