欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

[Typescript] Branded type

最编程 2024-03-16 18:10:52
...

For example, we have a type repersent currency: USD, value should be something like '$123', a string type with `$` and number.

 

So let's say we make a USD type as string

type USD = string

PORS: it is semantice

CONDs: I can give any value doesn't match the requiremenets of USD

for example: '#123'

 

To solve this problem we can use Branded type.

 

Define a Branded type

declare const brand: unique symbol;
type Brand<K, T> = T & {[brand]: K}

Now a branded USD type should be

type USD = Brand<'USD', string>
/*
string & {
  brand: 'USD'
}
*/

Now if we assign correct value to a branded USD type, we found it has error:

// @ts-expect-error
const price: USD = '$123' // '$123' is not assignable to USD

 

Use Branded type with Type guards and Type assertions

declare const brand: unique symbol;
type Brand<K, T> = T & {[brand]: K}

type USD = Brand<'USD', string>
function isUSD(str: string | USD): str is USD {
  return typeof str === 'string' && str.toString().startsWith('$') && !Number.isNaN(+str.substring(1))
}
function assertUSD(str: string | USD): asserts str is USD {
  const b = typeof str === 'string' && str.toString().startsWith('$') && !Number.isNaN(+str.substring(1))
  if(!b) {
    throw new Error('Not a USD currency')
  }
}

Usage with type guards:

const priceUSD = '$123'
const priceEUR = '€123'

function getUSDPayment(usd: USD) {
  console.log(usd)
}

// OK
if (isUSD(priceUSD)) {
 getUSDPayment(priceUSD)
}

// NOT
if (isUSD(priceEUR)) {
 getUSDPayment(priceEUR)
}

Usage with type assertion:

const priceUSD = '$123'
const priceEUR = '€123'

function payByUSD(usd: string) {
  assertUSD(usd)
  getUSDPayment(usd)
}

function getUSDPayment(usd: USD) {
  console.log(usd)
}

payByUSD(priceUSD)
payByUSD(priceEUR) // throw error

 

Resources:

Surviving the TypeScript Ecosystem — Part 6: Branding and Type-Tagging

Branded Types give you stronger input validation

https://github.com/kevinbgreene/typescript-demo/blob/branding/src/index.ts