Skip to content

Instantly share code, notes, and snippets.

@DenisFrezzato
Last active July 1, 2020 15:31
Show Gist options
  • Save DenisFrezzato/8ff7c5ed5b7d37d2554d3485c00d8452 to your computer and use it in GitHub Desktop.
Save DenisFrezzato/8ff7c5ed5b7d37d2554d3485c00d8452 to your computer and use it in GitHub Desktop.
Physical quantities with branded types. Example with monetary values.
type Branded<P, B> = P & { readonly _brand: B }
type PhysicalQuantity<Q, U> = Branded<number, Q> & U
type Currency<C> = PhysicalQuantity<'Currency', C>
type EUR_ = { readonly EUR: unique symbol }
type EUR = Currency<EUR_>
type USD_ = { readonly USD: unique symbol }
type USD = Currency<USD_>
type Money = EUR | USD
function mkMoney<M extends Money>(n: number): M {
return n as any
}
function add<M extends Money>(a: M, b: M): M {
return mkMoney<M>(a + b)
}
function convertTo<MO extends Money>(rate: number): <MI extends Money>(m: MI) => MO {
return (m) => mkMoney<MO>(m * rate)
}
const someEur = mkMoney<EUR>(3)
const someUsd = mkMoney<USD>(6)
add(someEur, someEur)
add(someUsd, someEur) // Error: Argument of type 'PhysicalQuantity<"Currency", EUR_>' is not assignable to parameter of type 'PhysicalQuantity<"Currency", USD_>'.
add(1, someUsd) // Error: Argument of type '1' is not assignable to parameter of type 'Money'.
convertTo<USD>(1.11)(someEur)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment