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:
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.
The error is cause by the fact that you are casting the response to "any[]". => You hase 2 solutions:
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)
}
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:
Try to use the RxJS observable without converting it to a Promise. Observables provides a range of advantages over Promises.
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
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP