Last active
September 27, 2020 22:36
-
-
Save disco0/b0455e9952163306ff4ca65af76d3e8a to your computer and use it in GitHub Desktop.
Typescript nominal typing scratch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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