Search code examples
typescriptmapped-types

How to initialize a property of a mapped typed in Typescript


Let's say I want to model a Fruit Shop in Typescript: I have a union type of string literals:

type Fruit = "apple" | "banana" | "cherry"

I have a fruit shop with a price list and stock register:

interface FruitShop1 {
  priceList: {
    apple: number,
    banana: number,
    cherry: number
  }
  stock: {
    apple: number,
    banana: number,
    cherry: number
  }
}

Since I will want to add lots of fruit later and I don't want to duplicate the properties manually on the priceList and stock properties, so I modified the FruitShop1 interface to use a mapped types based off Fruit:

interface FruitShop2 {
  priceList: { [fruit in Fruit]: number }
  stock: { [fruit in Fruit]: number }
}

I create my class implemeting FruitShop:

class FruitShopImpl implements FruitShop2 {
  priceList: { apple: number; banana: number; cherry: number; };
  stock: { apple: number; banana: number; cherry: number; };
}

However, I need to initialize pricelist and stock. How do I do this?

enter image description here

What I'd like is for every property in priceList be initialized to 1 and every property in stock to be initialized to 0.

I've tried interating over every property in a constructor, but this produces errors I think because priceList isn't initialized so it has no properties to iterate over. This feels like a chicken-and-egg catch-22 problem. Is there a solution, without manually initializing each property individually?

enter image description here


Solution

  • I've gone for a similar solution as suggested by @soffyo, but instead of using an array I'm using an enum backed by enums. Here's my code:

    enum Fruits {
      Apple = "apple",
      Banana = "banana",
      Cherry = "cherry"
    }
    
    interface IFruitShop {
      prices: { [fruit in Fruits]: number }
      stock: { [fruit in Fruits]: number }
    }
    
    class FruitShop implements IFruitShop {
      prices: IFruitShop['prices'];
      stock: IFruitShop['stock'];
    
      constructor() {
        this.prices = {} as { [fruit in Fruits]: number };
        this.stock = {} as { [fruit in Fruits]: number };
        Object.keys(Fruits).forEach(f => {
          this.prices[f as Fruits] = 1;
          this.stock[f as Fruits] = 0;
        });
      }
    }
    

    Typscript Playground

    I think string enum could be an advantage for me in my actual project.