2
votes

I am having this problem "Type '{ key: string; }' is not assignable to type 'Product'." when I add to an array products variable the Product interface type.

Here is my code:

product.ts

export interface Product {
    title: string;
    price: number;
    category: string;
    imageUrl: string;
}

productService.ts

import { Injectable } from '@angular/core';
import { AngularFireDatabase } from 'angularfire2/database';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class ProductService {

  constructor(private db: AngularFireDatabase) { }

  create(product){
    return this.db.list('/products').push(product);
  }

  getAll(){
    return this.db.list('/products').snapshotChanges()
      .pipe(
        map(actions =>
          actions.map(a => ({ key: a.key, ...a.payload.val() }))
        )
      );
  }

  get(productId){
    return this.db.object('/products/' + productId).valueChanges();
  }

  update(productId, product){
    return this.db.object('/products/' + productId).update(product);
  }

  delete(productId){
    return this.db.object('/products/' + productId).remove();
  }
}

admin-product.component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ProductService } from 'src/app/product.service';
import { Subscription } from 'rxjs';
import { Product } from 'src/app/models/product';

@Component({
  selector: 'app-admin-products',
  templateUrl: './admin-products.component.html',
  styleUrls: ['./admin-products.component.css']
})
export class AdminProductsComponent implements OnInit, OnDestroy {
  products: Product[];
  filteredProducts: any[];
  subscription: Subscription;
  dataSource;
  displayedColumns: string[] = ['title', 'price', 'action'];

  constructor(private productService: ProductService){
    this.subscription = this.productService.getAll().subscribe(products => this.filteredProducts = this.products = products);
  }

  ngOnInit() { }

  filter(query: string){
    if(query){
      this.filteredProducts = this.products.filter(p => p.title.toLowerCase().includes(query.toLowerCase()));
    }else{
      this.filteredProducts = this.products;
    }
  }

  ngOnDestroy(){
    this.subscription.unsubscribe();
  }
}

terminal error

ERROR in src/app/admin/admin-products/admin-products.component.ts(19,100): error TS2322: Type '{ key: string; }[]' is not assignable to type 'Product[]'.

src/app/admin/admin-products/admin-products.component.ts(19,100): error TS2322: Type '{ key: string; }[]' is not assignable to type 'Product[]'. Type '{ key: string; }' is not assignable to type 'Product'. Property 'title' is missing in type '{ key: string; }'.

Could anyone help me please?

4
what types are in observable returned from this.db.list('/products').snapshotChanges()?Przemyslaw Jan Beigert

4 Answers

2
votes

Just for an suggestion,

It seems something you have mapped and assigned value to a mentioned name(key).

getAll(){
    return this.db.list('/products').snapshotChanges()
      .pipe(
        map(actions =>
          actions.map(a => ({ key: a.key, ...a.payload.val() })) //here you have made something strange.
        )
      );
  }

Solution:- You need map proper interface model variable.

example,
  .map( {title: a.something;
    price: a.something;
    category: a.something;
    imageUrl: a.something
})
2
votes

Thank you for replay to all.

I got it working using this code with your help Karnan Muthukumar:

getAll(){
    return this.db.list('/products').snapshotChanges()
      .pipe(
        map(actions =>
          actions.map(a => ({ key: a.key, ...a.payload.val() } as Product))
        )
      );
  }
0
votes

You could console.log the observable returned in this.db.list('/products').snapshotChanges() and see what params are on it, probably there are more thing that only a Product so you will have to choose what you want from this Observable.

0
votes

The most likely problem is that, you are trying to use a generic object returned from your service as a typed Object. I like to use .map and Object.assign() to covert things, like this:

  getAll(){
    return this.db.list('/products').snapshotChanges()
      .pipe(
        map(actions =>
          actions.map(a => (Object.assign(new ClassThatImplementsProductInferface(), a))
        )
      );
  }

This assumes that the data returned from snapshotChanges() has all the data and could be a Product Interface.