我继承了一个 API,它为每个端点响应返回一个自定义
Response
包装器对象。我正在构建一个 Angular 应用程序来从此 API 获取数据,并在我的 Typescript 中定义了类似的 Response
类型。
export class Response<T> {
code: number;
data: T;
message?: string;
}
当我进行 API 调用时,我会收到 JSON 格式的相应响应。
import { HttpClient } from '@angular/common/http';
this.http.post<Response<Data>>(url, payload).subscribe({ // Type defined here (Data)
next: (response) => {
if (response.code === 200) {
this.data = response.data; // This is not typed, returns Object, but everything works
}
}
})
我注意到的第一件事是,当响应从 Angular http 调用返回时,它实际上并没有被输入。这只是一个
Object
,我无法强制执行其他扩展方法来验证响应是否成功返回或解析以进行进一步的错误处理。
我想出的一个解决方案是将此调用包装在我可以自己键入的内容中,以获取要使用的实际类型对象并处理任何错误(
Response
返回 null 或 BadDataException 等)
export function typeSubscribe<T>(
this: Observable<Response<T>>,
next?: (value: T) => void,
error?: (error: T) => void,
complete?: () => void
): Subscription {
let newNext: (value: T) => void = next;
if (next) {
newNext = (value: T): void => {
value = buildReponse<T>(value);
if (value.code !== 200) {
error(value.message);
return;
}
next(value.data);
};
}
const sub = this.subscribe({ next: newNext, error, complete });
return sub;
}
function buildReponse<T>(body: any): Response<T> {
const response = new Response<T>();
if (!body) {
response.code = 500;
response.message = 'Response is empty';
response.data = null;
return response;
}
response.code = body.code;
response.message = body.message;
response.data = map<T>(body.data, body.data.constructor); // body.data is Object or Array, whatever comes back from API
return response;
}
// This function just returns empty Object {}
function map<T>(values: any, ctor: new () => T): T {
const instance = new ctor();
return Object.keys(instance).reduce((acc: any, key: string) => {
acc[key] = values[key];
return acc;
}, {}) as T;
}
意图像这样使用它
this.http.post<Response<Data>>(url, payload).typeSubscribe((response) => {
this.data = response; // this.data will now be fully typed as `Data` and handled appropriately
})
我的问题:
typeSubscribe
知道T
是什么。尽管在 IDE 编译器中的某个地方,IntelliSense 在每一步都知道它,但它似乎在运行时不知道它。如何在代码中的此时在 Typescript 中实例化此类型(来自 Http 响应)。Response
对象并仅在成功时传递 data
,那就太好了。是否可以扩展订阅以具有一种类型(Response<T>
)的可观察对象并仅返回T
?订阅似乎需要 Observable 类型 this
来匹配从 next
返回的内容。可以把它们做成不同的类型吗?此时我并不担心正确的错误处理或日志记录,只是想让打字系统正常工作并理解此时的订阅。
我认为你正在增加复杂性来解决简单的事情!如果未识别,请添加类型,至于为什么会发生这种情况,我无法检查,除非它在 stackblitz 上复制,否则可能有多种原因,请检查下面的 stackblitz 工作正常,请尝试复制发出并分享回来。
临时修复
import { HttpClient } from '@angular/common/http';
this.http.post<Response<Data>>(url, payload).subscribe({ // Type defined here (Data)
next: (response: Data) => {
if (response.code === 200) {
this.data = response.data; // This is not typed, returns Object, but everything works
}
}
})
ts - 问题无法重现!
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import {
HttpClient,
HttpClientModule,
provideHttpClient,
} from '@angular/common/http';
import 'zone.js';
export class Response<T> {
code!: number;
data!: T;
message?: string;
}
export interface Data {
id: number;
title: string;
userId: number;
}
@Component({
selector: 'app-root',
standalone: true,
imports: [HttpClientModule],
template: `
<h1>Hello from {{ name }}!</h1>
<a target="_blank" href="https://angular.dev/overview">
Learn more about Angular
</a>
`,
})
export class App {
name = 'Angular';
data!: Data;
constructor(private http: HttpClient) {}
ngOnInit() {
this.http
.post<Response<Data>>('https://dummyjson.com/posts/add', {
title: 'I am in love with someone.',
userId: 5,
})
.subscribe({
// Type defined here (Data)
next: (response) => {
console.log(response);
if (response.code === 200) {
this.data = response!.data; // This is not typed, returns Object, but everything works
}
},
});
}
}
bootstrapApplication(App, {
providers: [provideHttpClient()],
});