简体   繁体   English

Typescript with Angular HttpClient ErrorType ...在调用类方法时不是函数

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

I'm facing some weird (to me) problem with objects and types in typescript/angular.我在 typescript/angular 中遇到了一些奇怪的(对我来说)对象和类型的问题。

I have some class describing my objects received from remote API:我有一些类描述从远程 API 接收到的对象:

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

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

I'm using HttpClient to get objects from API:我正在使用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
        }
    );
}

My problem is that when I try to call method doSomething on one of my Point from array我的问题是,当我尝试从数组中的一个点上调用方法doSomething

var firstPoint = points[0];
firstPoint.doSomething()

I get some weird error on console:我在控制台上收到一些奇怪的错误:

ERROR TypeError: firstPoint.doSomething is not a function

I'm not using Typescript and Angular for very long so I'm assuming that it is something wrong with my code but I couldn't find answer to my issue.我很久没有使用 Typescript 和 Angular,所以我假设我的代码有问题,但我找不到问题的答案。 Could you give me some advice?你能给我一些建议吗?

The problem is that you don' actually get instances of Point<\/code> from the web service.问题是您实际上并没有从 Web 服务中获取Point<\/code>的实例。 You get a JSON object that has the class fields, but not the methods.你得到一个 JSON 对象,它有类字段,但没有方法。 You can use instantiate the class and use Object.assign<\/code> to assign the values from the object literal to each Point<\/code> instance您可以使用实例化类并使用Object.assign<\/code>将对象文字中的值分配给每个Point<\/code>实例

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()<\/code> will solve your current problem, but not if you have nested objects. Object.assign()<\/code>将解决您当前的问题,但如果您有嵌套对象,则不会。 Then you will have to do Object.assign()<\/code> for each nested object as well, which can get tedious if you have to do this in multiple places in your codebase.然后,您还必须为每个嵌套对象执行Object.assign()<\/code> ,如果您必须在代码库的多个位置执行此操作,这可能会变得乏味。

I suggest an alternative: class-transformer<\/a> With this you can mark your nested fields with annotations that tell the compiler how to create the nested objects as well.我建议一个替代方案: class-transformer<\/a>使用它,您可以使用注释标记嵌套字段,这些注释告诉编译器如何创建嵌套对象。 With this you only need to use the plainToClass()<\/code> method to map your top level object and all the underlying fields will also have the correct types\/objects.有了这个,您只需要使用plainToClass()<\/code>方法来映射您的顶级对象,所有底层字段也将具有正确的类型\/对象。

Example例子<\/h2>

Let's say we have two classes:假设我们有两个类:

 class Parent { name: string; child: Child; public getText(): string { return 'parent text'; } } class Child{ name: string; public getText(): string { return 'child text'; } }<\/code><\/pre>

The first case<\/strong> we already know doesn't work:我们已经知道的第一种情况<\/strong>不起作用:

 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<\/code><\/pre>

Second case<\/strong> using Object.assign()<\/code> :使用Object.assign()<\/code>第二种情况<\/strong>:

 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!<\/code><\/pre>

to make it work, we would have to do the following:为了使其工作,我们必须执行以下操作:

 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<\/code><\/pre>

Third case<\/strong> with class-transformer:使用类转换器的第三种情况<\/strong>:

First modify the parent class so that the child mapping is defined:首先修改父类,以便定义子映射:

 class Parent { name: string; @Type(() => Child) child: Child; public getText(): string { return 'parent text'; } }<\/code><\/pre>

then you can map to the parent object:然后你可以映射到父对象:

 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<\/code><\/pre>"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM