Skip to content

Instantly share code, notes, and snippets.

@castarco
Last active April 6, 2021 10:10
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 castarco/f720013b13f2572bf41eaf41296bdbb5 to your computer and use it in GitHub Desktop.
Save castarco/f720013b13f2572bf41eaf41296bdbb5 to your computer and use it in GitHub Desktop.
// We use symbols to eliminate any chance for problems with serialization
const __BaseType: unique symbol = new Symbol('__BaseType')
const __Brand: unique symbol = new Symbol('__Brand')
// We mark the "brand/flavor" fields as readonly to avoid anyone doing weird stuff with them
// We add a "__BaseType" field to make possible "complex" type manipulations
// We accept "symbol" tags as a mechanism to avoid "types forgery" to bypass the type checker,
// although I foresee that most of the times it won't be used.
export type Branded<BaseType, Tag extends string | symbol> = BaseType & {
readonly [__BaseType]: BaseType
readonly [__Brand]: Tag
}
export type Flavored<BaseType, Tag extends string | symbol> = BaseType & {
readonly [__BaseType]?: BaseType
readonly [__Brand]?: Tag
}
export type OurBrand = Branded<number, 'AnAnswer'>
export type OurFlavor = Flavored<number, 'AnAnswer'>
const primitive = 23 // ok, of course
const ourFirstBrandedValue: OurBrand = 42 as OurBrand // ok, of course
const ourSecondBrandedValue: OurBrand = 42 // error, it does not accept "unbranded" values
const ourThirdBrandedValue: OurBrand = 42 as OurFlavor // error, it does not accept flavored values
const ourFirstFlavoredValue: OurFlavor = 42 as OurFlavor // ok, of course
const ourSecondFlavoredValue: OurFlavor = 42 as OurBrand // luckily for us, ok too
const ourThirdFlavoredValue: OurFlavor = 42 // ok too, then, why are flavored types good for if it accepts "everything"?
export type TheirBrand = Branded<number, 'AQuestion'>
export type TheirFlavor = Flavored<number, 'AQuestion'>
// This is where flavored types are useful
const ourFourthFlavoredValue: OurFlavor = 42 as TheirBrand // error, so, flavored types are useful for something
const ourFifthFlavoredValue: OurFlavor = 42 as TheirFlavor // error as well
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment