title: Angular TypeScript: "Type '(string | undefined)[]' is not assignable to type 'string[]'."
body: <p>I'm coding along with the book <em>Pro Angular 9</em>, by Adam Freeman. Line 8 is throwing an error:</p>
  <pre class="lang-js prettyprint-override"><code>private categories: string[] = [];
  </code></pre>
  <p>The error is</p>
  <pre><code>Error: src/app/model/product.repository.ts:13:7 - error TS2322:
  Type '(string | undefined)[]' is not assignable to type 'string[]'.
    Type 'string | undefined' is not assignable to type 'string'.
      Type 'undefined' is not assignable to type 'string'.
  </code></pre>
  <p>I have no idea what that line means. How can a variable be a string and an array?</p>
  <p>To confuse me further, Angular says the error is in line 13, not line 8. Here's line 13:</p>
  <pre class="lang-js prettyprint-override"><code>this.categories = data.map(p =&gt; p.category).filter((c, index, array) =&gt; array.indexOf(c) == index).sort();
  </code></pre>
  <p>I don't know what that line does, other than run a map over an array with a filter.</p>
  <p>The file has two more errors. Line 17:</p>
  <pre class="lang-js prettyprint-override"><code>getProducts(category: string = null): Product[] {
  </code></pre>
  <pre><code>Type 'null' is not assignable to type 'string'.
  
  </code></pre>
  <p>In JavaScript I would make an empty string: <code>var myString = ''</code>. In TypeScript why would you make a string null? Aren't <code>string</code> and <code>null</code> different types?</p>
  <p>The third error is in line 22:</p>
  <pre class="lang-js prettyprint-override"><code>return this.products.find(p =&gt; p.id == id);
  </code></pre>
  <pre><code>Type 'Product | undefined' is not assignable to type 'Product'.
    Type 'undefined' is not assignable to type 'Product'.
  </code></pre>
  <p>I have no idea what that means.</p>
  <p>Here's the entire file:</p>
  <pre class="lang-js prettyprint-override"><code>import { Injectable } from &quot;@angular/core&quot;;
  import { Product } from &quot;./product.model&quot;;
  import { StaticDataSource } from &quot;./static.datasource&quot;;
  
  @Injectable()
  export class ProductRepository {
    private products: Product[] = [];
    private categories: string[] = [];
  
    constructor(private dataSource: StaticDataSource) {
      dataSource.getProducts().subscribe(data =&gt; {
        this.products = data;
        this.categories = data.map(p =&gt; p.category).filter((c, index, array) =&gt; array.indexOf(c) == index).sort();
      });
    }
  
    getProducts(category: string = null): Product[] {
      return this.products.filter(p =&gt; category == null || category == p.category);
    }
  
    getProduct(id: number): Product {
      return this.products.find(p =&gt; p.id == id);
    }
  
    getCategories(): string[] {
      return this.categories;
    }
  }
  </code></pre>
  <p>Here's <code>product.model.ts</code>:</p>
  <pre class="lang-js prettyprint-override"><code>export class Product {
  
    constructor(
      public id?: number,
      public name?: string,
      public category?: string,
      public description?: string,
      public price?: number
    ) { }
  }
  </code></pre>
  <p>That threw another error:</p>
  <p><code>Unexpected token. A constructor, method, accessor, or property was expected.</code></p>
  <p>Is the error on line 22 due to an error in the <code>Product</code> class? What's the error in the <code>Product</code> class? It looks like the class has a constructor.</p>
  <p>I went to the book's GitHub repo and downloaded the latest files, which are the same as in the book.</p>