Skip to content

Instantly share code, notes, and snippets.

@unscriptable
Last active December 5, 2015 05:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save unscriptable/03f20da58766158f74c9 to your computer and use it in GitHub Desktop.
Save unscriptable/03f20da58766158f74c9 to your computer and use it in GitHub Desktop.
flow Cart
'use strict';
/* @flow */
// Cart items.
type SKU = string;
type Item = {
sku: SKU,
quantity: number
};
type Items = Map<SKU, Item>;
// Cart states. In lieu of enums, these are constant instances.
class Empty {}
const empty = new Empty();
class Loaded {}
const loaded = new Loaded();
class Paid extends Loaded {}
const paid = new Paid();
// Cart, parameterized by state and Maybe Items (null | Items).
class Cart<state: Empty | Loaded | Paid> {
constructor (state: Empty | Loaded | Paid, items: ?Items) {
this.items = items != null ? items : new Map();
}
items: Items;
}
// Exported cart functions.
export const create
: () => Cart<Empty>
= () => new Cart(empty);
export const addItem
: (cart: Cart<Empty> | Cart<Loaded>, item: Item) => Cart<Loaded>
= (cart, item) => {
const items = cart.items;
if (items.has(item.sku)) {
throw new Error(`Item is already in cart: ${ item.sku }.`);
}
items.set(item.sku, item);
return new Cart(loaded, items);
};
export const removeItem
: (cart: Cart<Loaded>, item: Item) => Cart<Loaded> | Cart<Empty>
= (cart, item) => {
const items = cart.items;
if (!items.has(item.sku)) {
throw new Error(`Item is not in cart: ${ item.sku }.`);
}
items.delete(item.sku);
return items.count === 0
? new Cart(empty)
: new Cart(loaded, items);
};
export const replaceItem
: (cart: Cart<Loaded>, item: Item) => Cart<Loaded>
= (cart, item) => {
const items = cart.items;
if (!items.has(item.sku)) {
throw new Error(`Item is not in cart: ${ item.sku }.`);
}
items.set(item.sku);
return new Cart(loaded, items);
};
export const pay
: (cart: Cart<Loaded>) => Cart<Paid>
= (cart) => {
// do some payments stuff here
return new Cart(paid, cart.items);
};
// Example code.
let cart = create();
cart = addItem(cart, { sku: '123213', quantity: 2 });
// this line should fail to type-check:
cart = removeItem(create(), { sku: '123213', quantity: 2 });
'use strict';
/* @flow */
/*::
type SKU = string;
type Item = {
sku: SKU,
quantity: number
};
type Items = Map<SKU, Item>;
*/
class EmptyCart {}
class LoadedCart {
constructor (items/*: Items*/) {
this.getItems = function () { return items; };
}
/*:: getItems: () => Items;*/
}
class PaidCart extends LoadedCart {}
// type AnyCart = EmptyCart | LoadedCart | PaidCart;
export const create
/*: () => EmptyCart*/
= () => new EmptyCart();
export const extractItems
/*: (cart: LoadedCart) => Items*/
= cart => cart.getItems();
export const addItem
/*: (cart: EmptyCart | LoadedCart, item: Item) => LoadedCart*/
= (cart, item) => {
const items = cart instanceof EmptyCart
? (new Map()/*: Items*/)
: cart.getItems();
if (items.has(item.sku)) {
throw new Error(`Item is already in cart: ${ item.sku }.`);
}
items.set(item.sku, item);
return new LoadedCart(items);
};
export const removeItem
/*: (cart: LoadedCart, item: Item) => LoadedCart | EmptyCart*/
= (cart, item) => {
const items = cart.getItems();
if (!items.has(item.sku)) {
throw new Error(`Item is not in cart: ${ item.sku }.`);
}
items.delete(item.sku);
return items.count === 0
? new EmptyCart()
: new LoadedCart(items);
};
export const replaceItem
/*: (cart: LoadedCart, item: Item) => LoadedCart | EmptyCart*/
= (cart, item) => {
const items = cart.getItems();
if (!items.has(item.sku)) {
throw new Error(`Item is not in cart: ${ item.sku }.`);
}
items.set(item.sku);
return new LoadedCart(items);
};
export const pay
/*: (cart: LoadedCart) => PaidCart*/
= (cart) => {
// do some payments stuff here
return new PaidCart(cart.getItems());
};
let cart = create();
cart = addItem(cart, { sku: '123213', quantity: 2 });
// this line should fail to type-check:
cart = removeItem(create(), { sku: '123213', quantity: 2 });
'use strict';
/* @flow */
type SKU = string;
type Item = {
sku: SKU,
quantity: number
};
type Items = Map<SKU, Item>;
class EmptyCart {}
class LoadedCart {
constructor (items: Items) {
this.getItems = function () { return items; };
}
getItems: () => Items;
}
class PaidCart extends LoadedCart {}
// type AnyCart = EmptyCart | LoadedCart | PaidCart;
export const create
: () => EmptyCart
= () => new EmptyCart();
export const extractItems
: (cart: LoadedCart) => Items
= cart => cart.getItems();
export const addItem
: (cart: EmptyCart | LoadedCart, item: Item) => LoadedCart
= (cart, item) => {
const items = cart instanceof EmptyCart
? (new Map(): Items)
: cart.getItems();
if (items.has(item.sku)) {
throw new Error(`Item is already in cart: ${ item.sku }.`);
}
items.set(item.sku, item);
return new LoadedCart(items);
};
export const removeItem
: (cart: LoadedCart, item: Item) => LoadedCart | EmptyCart
= (cart, item) => {
const items = cart.getItems();
if (!items.has(item.sku)) {
throw new Error(`Item is not in cart: ${ item.sku }.`);
}
items.delete(item.sku);
return items.count === 0
? new EmptyCart()
: new LoadedCart(items);
};
export const replaceItem
: (cart: LoadedCart, item: Item) => LoadedCart | EmptyCart
= (cart, item) => {
const items = cart.getItems();
if (!items.has(item.sku)) {
throw new Error(`Item is not in cart: ${ item.sku }.`);
}
items.set(item.sku);
return new LoadedCart(items);
};
export const pay
: (cart: LoadedCart) => PaidCart
= (cart) => {
// do some payments stuff here
return new PaidCart(cart.getItems());
};
let cart = create();
cart = addItem(cart, { sku: '123213', quantity: 2 });
// this line should fail to type-check:
cart = removeItem(create(), { sku: '123213', quantity: 2 });
@unscriptable
Copy link
Author

Other indentation styles:

export const extractItems: (cart: LoadedCart) => Items =
    cart => cart.getItems();
export const extractItems
    : (cart: LoadedCart) => Items
    = cart => cart.getItems();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment