0

In our country there are two currencies, rial and toman, which can be converted into each other (1 toman = 10 rial), I want to create a specific type for each currency, that to be unassignable. I don't want to lose any performance (for example by using objects instead of number)

example code:

type toman = number
type rial = number

function x(r: rial): void {
   console.log(r)
}


const t: toman = 5
x(t) // I want to get an error here, because toman is unassignable to rial

ERFANIUM
  • 41
  • 1
  • 5
  • You can't. *Encapsulate* the value instead, for example `class Toman { constructor(readonly value: number) {} }`. – jonrsharpe Aug 18 '20 at 21:54
  • @jonrsharpe The idea also came to me, but it is not optimal in terms of performance – ERFANIUM Aug 18 '20 at 22:00
  • Then you'll have to decide which you prefer, but those are your choices. Please note that you should include what you've considered and dismissed and why *in your question*. – jonrsharpe Aug 18 '20 at 22:02
  • There is a similar question here, https://stackoverflow.com/questions/26810574/is-there-a-way-to-create-nominal-types-in-typescript-that-extend-primitive-types but as far as as read there is no solution yet. – max-lt Aug 18 '20 at 22:34

3 Answers3

1

This one works, but it's not a pretty solution!

interface Toman {
   n: 'toman'
}

interface Rial {
   n: 'rial'
}

type toman = Toman | number
type rial = Rial | number

function x(r: rial): void {
   console.log(r)
}

const t = 5 as toman
x(t)
ERFANIUM
  • 41
  • 1
  • 5
1

Typescript has structural types, so if you want two types to be unassignable to each other then they must have incompatible structures. We can achieve this by giving the types extra properties to distinguish them; these properties won't really exist at runtime, so there is no performance penalty.

Note that you need a type assertion like 5 as toman at the place the value is assigned.

type toman = number & { __toman: toman }
type rial = number & { __rial: rial }

function x(r: rial): void {
   console.log(r)
}

const t = 5 as toman
x(t) // error: property '__rial' is missing

Playground Link

kaya3
  • 47,440
  • 4
  • 68
  • 97
0

I can't see any reason for it since both have the same type: numbers. However, you could do something like:

type Currency<PropertyName extends string> = {
    [key in PropertyName]: number
}

function x(r: Currency<'rial'>): void {
   console.log(r)
}


const t = {toman: 5} // change to { rial: 5 } to fix the ts error below
x(t) // You you get an error