带有 Angular HttpClient ErrorType 的 Typescript ... 在调用 class 方法时不是函数

Typescript with Angular HttpClient ErrorType ... is not a function when calling class method

我在 typescript/angular.

中遇到对象和类型的一些奇怪的(对我来说)问题

我有一些 class 描述了我从远程接收的对象 API:

export class Point {
    lat:number;
    lng:number;
    name:string;

    public doSomething() {
        console.log("doSomething called");
    }
}

我正在使用 HttpClient 从 API:

获取对象
constructor(private http:HttpClient) {}

getPointsAsync(callback: (points: Point[]) => {}) {
    this.http.get<Point[]>(`${environment.apiUrl}/api/points`)
    .subscribe(
        (result: Point[]) => {
            //do something with data
            callback(result);
        },
        error => { //some error handling
        }
    );
}

我的问题是,当我尝试在数组

中的一个点上调用方法 doSomething
var firstPoint = points[0];
firstPoint.doSomething()

我在控制台上收到一些奇怪的错误:

ERROR TypeError: firstPoint.doSomething is not a function

我很长时间没有使用 Typescript 和 Angular,所以我假设我的代码有问题,但我找不到问题的答案。你能给我一些建议吗?

问题是您实际上并没有从 Web 服务中获取 Point 的实例。你得到一个 JSON 对象,它有 class 字段,但没有方法。您可以使用实例化 class 并使用 Object.assign 将对象文字中的值分配给每个 Point 实例

getPointsAsync(callback: (points: Partial<Point>[]) => {}) {
    this.http.get<Point[]>(`${environment.apiUrl}/api/points`)
        .subscribe(
            (result: Partial<Point>[]) => {
                let points = result.map(p=> Object.assign(new Point(), p));
                callback(points);
            },
            error => { //some error handling
            }
        );
}

Object.assign() 将解决您当前的问题,但如果您有嵌套对象则不会。然后您还必须为每个嵌套对象执行 Object.assign(),如果您必须在代码库的多个位置执行此操作,这会变得乏味。

我建议一个替代方案:class-transformer 有了这个,您可以使用注释标记嵌套字段,告诉编译器如何创建嵌套对象。有了这个,您只需要使用 plainToClass() 方法来映射您的顶级对象,所有基础字段也将具有正确的 types/objects.

例子

假设我们有两个 classes:

class Parent {
    name: string;
    child: Child;

    public getText(): string {
        return 'parent text';
    }
}

class Child{
    name: string;

    public getText(): string {
        return 'child text';
    }
}

我们已经知道的第一种情况不起作用:

let parentJson: any = {name: 'parent name', child: {name: 'child name'}};
let parent: Parent = parentJson; // note: compiler accepts this because parentJson is any.  
        // If we tried to assign the json structure directly to 'parent' it would fail because the compiler knows that the method getText() is missing!

console.log(parent.getText()); // throws the error that parent.getText() is not a function as expected

第二种情况 使用Object.assign():

let parentJson: any = {name: 'parent name', child: {name: 'child name'}};
let parent: Parent = Object.assign(parentJson); 

console.log(parent.getText()); // this works
console.log(parent.child.getText()); // throws error that parent.child.getText() is not a function!

要使其正常工作,我们必须执行以下操作:

let parentJson: any = {name: 'parent name', child: {name: 'child name'}};
let parent: Parent = Object.assign(parentJson);
parent.child = Object.assign(parentJson.child);

console.log(parent.getText()); // this works
console.log(parent.child.getText()); // this works

第三种情况与class-transformer:

首先修改父class以便定义子映射:

class Parent {
    name: string;
    @Type(() => Child)
    child: Child;

    public getText(): string {
        return 'parent text';
    }
}

然后你可以映射到父对象:

let parentJson: any = {name: 'parent name', child: {name: 'child name'}};
let parent: Parent = plainToClass(Parent, parentJson);

console.log(parent.getText()); // this works
console.log(parent.child.getText()); // this works