TransWikia.com

angular Property 'value' does not exist on type 'Object'

Stack Overflow Asked by HomeAlone on January 14, 2021

In my Angular app I have an issue with the response of an API call:

 private async getActor(actor:string) {
    const response = await this.searchActor.getActorImage(actor).toPromise()
    let actPics = []    
      for (var val of (response as any[]).value) {
        actPics.push(val.thumbnailUrl)}                   
  }

My app runs fine but I have this message in the console when I run ng serve

Error:
src/app/quiz-editor/actor-question/actor-question.component.ts:55:41 –
error TS2551: Property ‘value’ does not exist on type ‘any[]’. Did you
mean ‘values’?

55 for (var val of (response as any[]).value) {
~~~~~

node_modules/typescript/lib/lib.es2015.iterable.d.ts:75:5
75 values(): IterableIterator;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
‘values’ is declared here.

When I console.log(response) I get:

console.log(response)

I did some research and I read that I should declare response but how should I do it?.

I’ve tried this solution:

private async getActor(actor:string) {
    const response: { value:any[] } = await this.searchActor.getActorImage(actor).toPromise()
    let actPics = []      
    for (let val of response.value) {
      actPics.push(val.thumbnailUrl)}
}

But now I have this error:

 Error: src/app/quiz-editor/actor-question/actor-question.component.ts:55:11 - error TS2696: The 'Object' type is assignable to very few other types. Did you mean to use the 'any' type instead?
      Property 'value' is missing in type 'Object' but required in type '{ value: any[]; }'.

55     const response: { value:any[] } = await this.searchActor.getActorImage(actor).toPromise()
             ~~~~~~~~

  src/app/quiz-editor/actor-question/actor-question.component.ts:55:23
    55     const response: { value:any[] } = await this.searchActor.getActorImage(actor).toPromise()
                             ~~~~~
    'value' is declared here.

5 Answers

The error is cause by the fact that you are casting the response to "any[]". => You hase 2 solutions:

  1. you remove the casting (it is not necessary):

    private async getActor(actor:string) { const {value} = await this.searchActor.getActorImage(actor).toPromise() const actPics = value.map(val => val.thumbnailUrl)
    }

  2. You declare an interface representing the type of your API response

Answered by davidr on January 14, 2021

You're wrongfully trying to define the response as an array. Javascript Array obviously does not have value property or method. You need to define the following interface instead.

export interface IResponse {
  currentOffset: number;
  instrumentation: any;
  nextOffset: number;
  queryContext: any;
  readLink: string;
  totalEstimatedMatches: any;
  value: Array<any>;
  webSearchUrl: string;
  _type: string;
}

In an ideal case, you would also define similar interfaces for instrumentation, queryContext and totalEstimatedMatches as well as for the object contained in the value array and use them instead of using any.

Additionally you don't need an explicit loop to get an array of values from an object. You could use Array#map method instead.

export interface IResponse {
  currentOffset: number;
  instrumentation: any;
  nextOffset: number;
  queryContext: any;
  readLink: string;
  totalEstimatedMatches: any;
  value: Array<any>;
  webSearchUrl: string;
  _type: string;
}

@Component({...})
export class SomeComponent implements OnInit, OnDestroy {
  private async getActor(actor:string) {
    const response: IResponse = await this.searchActor.getActorImage(actor).toPromise();
    let actPics: Array<string> = response.value.map(val => val['thumbnailUrl']);
  }
}

Further remarks:

  1. Try to use the RxJS observable without converting it to a Promise. Observables provides a range of advantages over Promises.

  2. toPromise() method will be deprecated in RxJS 7 and will be gone in RxJS 8.

Answered by Michael D on January 14, 2021

You are declaring response as an array of type any, which is incorrect. response is not an array, response.value is an array. The any keyword tells typescript to not bother with type checking. From the docs:

In some situations, not all type information is available or its declaration would take an inappropriate amount of effort. These may occur for values from code that has been written without TypeScript or a 3rd party library. In these cases, we might want to opt-out of type checking. To do so, we label these values with the any type

So, the quick and dirty fix is to tell typescript that response is of type any doing something like this:

 private async getActor(actor:string) {
    const response = await this.searchActor.getActorImage(actor).toPromise()
    let actPics = []    
      for (var val of (response as any).value) {
        actPics.push(val.thumbnailUrl)}                   
  }

Another perhaps more Angular-correct way of doing things would be to create a model and use that as your type. Here's a stripped-down model:

export class ImageSearchResult {
    // you could/should type this as well instead of being an array of 
    // type any. It might be something like value: []ImageData;
    value: []any; 
}

Then, you use your model type instead of any:

 private async getActor(actor:string) {
    const response = await this.searchActor.getActorImage(actor).toPromise()
    let actPics = []    
      for (var val of (response as ImageSearchResult).value) {
        actPics.push(val.thumbnailUrl)}                   
  }

But going even further, your service should return the correct type. It's not the job of modules that consume services to type the data returned by services. Also - I am assuming you are returning an observable. Why call toPromise? Let's create a service function that uses Angular's http service and returns an observable of the correct type. You probably have something like this already:

  import { HttpClient } from '@angular/common/http';
  
  constructor(private http: HttpClient) { }

  /**
   * Search for images by actor. Returns an observable of type ImageSearchResult.
   * @param actor The name of the actor you want to search
   */
  getActorImage(actor: string): Observable<ImageSearchResult> {
    // Refactor to appropriate http action type (post/push/etc) and url.
    return this.http.get<ImageSearchResult>(`https://my-api.com/actor-search?q=${actor}`);
  }

Then to consume this service function:

private getActor(actor:string) {
  this.searchActor.getActorImage(actor).subscribe(response => {
    let actPics = []
    // No casing necessary, response is already correct type.
    for (var val of response.value) {
      actPics.push(val.thumbnailUrl)
    }
  });
}

Finally, it looks like you were possibly trying to use the Azure image search API. In that case, take a look at how they type the return objects for inspiration. Depending on your project's license, you could probably just import that project and use their models with your services.

Answered by Dean on January 14, 2021

You have to tell typescript what type your array is. By saying response as any[] the compiler does not know which objects your array contains. It may work at runtime, because typescript then "finds" the requested property. So what you should do, is create a class/interface to define the object, that you expect to receive from your API and then parse the response to it.

Example:

private async getActor(actor:string) {
const response = await this.searchActor.getActorImage(actor).toPromise();
const images = response as actorImage[];
let actPics = [];    
  for (var val of images) {
    actPics.push(val.thumbnailUrl)}                   

}

interface actorImage{
  thumbnailUrl: string   
}

Answered by mb1337 on January 14, 2021

As your log statement shows, your response is an object containing value as a key. As such, you can do the following:

const response: { value: any[] } = await this.searchActor.getActorImage(actor).toPromise();
let actPics = [];
for (let val of response.value) {
  actPics.push(val.thumbnailUrl);                   
}

Answered by tilo on January 14, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP