Skip to content

Instantly share code, notes, and snippets.

@disco0
Last active September 27, 2020 22:36
Show Gist options
  • Save disco0/b0455e9952163306ff4ca65af76d3e8a to your computer and use it in GitHub Desktop.
Save disco0/b0455e9952163306ff4ca65af76d3e8a to your computer and use it in GitHub Desktop.
Typescript nominal typing scratch
/**
* Inspired by [Drew Colthorp's blog post, Flavoring: Flexible Nominal Typing for TypeScript](https://spin.atomicobject.com/2018/01/15/typescript-flexible-nominal-typing)
*/
/**
* Last form of an attempt to define a generic constrained to `unique symbol` for
* the property symbol (`NominalIdentifierSymbol` in this case).
*
* 'NominalIdentifierSymbol' is declared but its value is never read.(6133)
* VVVVVVVVVVVVVVVVVVVVVV
*/
interface NominalSchema<NominalLiteral, NominalIdentifierSymbol>
{
[NominalIdentifierSymbol]?: NominalLiteral
}
/**
* (Original plan was to use a generic similar to above to define
* NominalPattern / Nominal as a partial application using
* newly defined unique symbol `NominalSymbol`)
*/
const NominalSymbol: unique symbol = Symbol.for('NominalProperty');
interface NominalPattern<NominalLiteral>
{
[NominalSymbol]?: NominalLiteral
}
type Nominal<BaseType, NominalLiteral>
= BaseType & NominalPattern<NominalLiteral> // { [NominalSymbol]: NominalLiteral }
type AbsoluteNominal<BaseType, NominalLiteral>
= BaseType & { [NominalSymbol]: NominalLiteral }
/**
* @todo
* Shape of function that validates a defined Nominal type
*/
type NominalValidation<Base, NominalKind extends Base> = (obj: Base) => obj is NominalKind;
type Real = Nominal<number, "Number#Real">
/**
* Original definition
*
* ```` ts
*
* type Nominal<K, T> = K & { __brand?: T }
*
* ````
*/
//#region Test
type USD = Nominal<number, "USD">
type EUR = Nominal<number, "EUR">
const amoney = 10;
const usd = 10 as USD;
const eur = 10 as EUR;
// @ts-expect-error ts(2367) This condition will always return 'false' since the types 'Nominal<number, "USD">' and 'Nominal<number, "EUR">' have no overlap
let notSame = usd === eur;
// @ts-expect-error ts(2322) Type 'boolean' is not assignable to type 'number'
let badAssign: number = notSame
function gross(net: USD, tax: USD): USD { return net + tax }
gross(usd, usd); // ok
// @ts-expect-error ts(2345) Type '"EUR"' is not assignable to type '"USD"'
gross(eur, usd);
gross(amoney, usd); // ok
//#endregion Test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment