在带有 TypeScript 的 MobX 中使用 Set()?

Use Set() in MobX with TypeScript?

我有一个简单的 Grocery Shopping 应用程序,我想在其中使用 Set(),但是当我尝试使用 Set() 时遇到各种错误。我正在使用带有 TypeScript 的 MobX。

types.ts

export type Item = {
  name: string;
  instock: number;
  price: number;
};

export type Buyer = {
  cash: number;
  items: Array<Pick<Item, 'name'> & { quantity: number }>;
};

export type IStore = {
  items: Item[];
  buyer: Buyer;
};

store.ts

import { observable, action, set, computed, makeObservable } from 'mobx';

import type { Item, IStore, Buyer } from './types';

export class Store implements IStore {
  items: Item[] = [
    {
      name: "Egg",
      instock: 30,
      price: 4
    },
    {
      name: "Apple",
      instock: 24,
      price: 20
    },
    {
      name: "Banana",
      instock: 60,
      price: 8
    }
  ]

  buyer: Buyer = {
    cash: 50,
    items: [],
  }

  constructor() {
    makeObservable(this, {
      items: observable,
      buyer: observable,
      buyItem: action.bound,
    });
  }

  buyItem(name: string, price: number, quantity: number) {
    if (this.buyer.cash - price * quantity > 0) {
      this.buyer.items.push({ name, quantity })
      this.buyer.cash = this.buyer.cash - price * quantity
    }
  }
}

export const store = new Store();

我想将 buyer 设为 Set()。例如,types.ts 应该将 buyer 变成:

.
.
.
export type IStore = {
  .
  .
  .
  buyer: new Set<Buyer>();
};

同样,我应该可以更改 store.tsThe docs 没有完整的示例。我该怎么做?

Stackblitz 再现 → https://stackblitz.com/edit/react-mobx-set-not-working?file=store.ts

我使用 Map 而不是 Set,但是如果你想用 Set 替换 Map,它也可以完美地工作。

我使用 Map.has() 来检查存储的项目 name 是否是唯一的,因此它的行为类似于 Set

可以找到 Stackblitz → https://stackblitz.com/edit/react-mobx-grocery-shopping?file=index.tsx

types.ts

export type Item = {
  name: string;
  instock: number;
  price: number;
};

export type BuyerItem = Pick<Item, 'name'> & { quantity: number };

export type Buyer = {
  cash: number;
  items: Map<string, number>;
};

export type IStore = {
  items: Item[];
  buyer: Buyer;
};

store.ts

import { observable, action, makeObservable } from 'mobx';

import type { Item, IStore, BuyerItem, Buyer } from './types';

export class Store implements IStore {
  items: Item[] = [
    {
      name: "Egg",
      instock: 30,
      price: 4
    },
    {
      name: "Apple",
      instock: 24,
      price: 20
    },
    {
      name: "Banana",
      instock: 60,
      price: 8
    }
  ]

  buyer: Buyer = {
    cash: 120,
    items: new Map<string, number>(),
  }

  constructor() {
    makeObservable(this, {
      items: observable,
      buyer: observable,
      buyItem: action.bound,
    });
  }

  buyItem(name: string, price: number, quantity: number) {
    if (this.buyer.cash - price * quantity >= 0) {
      if (this.buyer.items.has(name)) {
        const alreadyBought = this.buyer.items.get(name)
        this.buyer.items.set(name, quantity + alreadyBought)
      }
      else
        this.buyer.items.set(name, quantity)
      this.buyer.cash = this.buyer.cash - price * quantity
    }
  }
}

export const store = new Store();

index.tsx

import React, { Component } from 'react';
import { render } from 'react-dom';
import { observer } from "mobx-react";

import { useStore } from "./context";

const App = observer(() => {
  const { items, buyer, buyItem } = useStore()
  const buyerItems = Array.from(buyer.items)
  
  return (
    <div>
      <h1>Grocery Shopping</h1>
      <table style={{ width: 400, lineHeight: 2, border: "1px solid black", textAlign: "center" }}>
        <thead>
          <tr>
            <th>Item</th>
            <th>Price</th> 
            <th>In Stock</th>
            <th>Purchase</th>
          </tr>
        </thead>
        <tbody>
        {items.map(item => (
            <tr key={item.name}>
              <td>{item.name}</td>
              <td>${item.price}</td>
              <td>{item.instock}</td>
              <td>
                <button onClick={() => {
                  buyItem(item.name, item.price, 1)
                }}>
                  Buy 1
                </button>
              </td>
            </tr>
          ))}
          </tbody>
      </table>
      <h2>${buyer.cash} balance left</h2>
      {buyerItems.length > 0 && (
        <>
          <h2>Customer Bought:</h2>
          <ul>
            {buyerItems.map(item => {
              return <li key={item[0]}>{item[1]} {item[0]}</li>
            })}
          </ul>
        </>
      )}
    </div>
  )
})

render(<App />, document.getElementById('root'));