Last active
June 1, 2020 07:12
-
-
Save doorgan/64de31fd8b6ac1a844941aec5ec95c22 to your computer and use it in GitHub Desktop.
Daggy's typed unions
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
// @ts-check | |
import union from "./union.js" | |
const Option = union("Option", { | |
Some: ["value"], | |
None: [] | |
}); | |
const val = Option.Some(5) | |
val.match({ | |
Some: value => console.log("Got value", value), | |
None: () => console.log("Got nothing!") | |
}) | |
val.match({ | |
Some: () => {} | |
// Property 'None' is missing in type '{ Some: () => void; }' but required | |
// in type '{ Some: Function; None: Function; }' | |
}) |
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
// @ts-check | |
import {taggedSum} from "daggy"; | |
/** | |
* An object mapping the keys of `T` to a function, such that the match function | |
* can make a static exhaustive check | |
* | |
* @template T | |
* @typedef MatchClauses | |
* @type {{[K in keyof T]: Function}} | |
*/ | |
/** | |
* The functiosn declared in the prototype of a Union. | |
* | |
* @template T | |
* | |
* @typedef UnionProtoFuns | |
* @type {Object} | |
* | |
* @prop {(any) => any} cata | |
* Same as match | |
* | |
* @prop {toString} toString | |
* Returns a stringified version of the type. | |
* | |
* @prop {(clauses: MatchClauses<T>) => any} match | |
* Matches on the union type to branch the logic. Each branch takes a function | |
* with the union values as arguments(in the order they were defined), that | |
* will be evaluated if that branch matches the union's type. | |
* | |
* Match enforces exchaustiveness checking, so there must be a branch for every | |
* type, otherwise match will raise an error. | |
*/ | |
/** | |
* The functions that are part of the Union constructor | |
* @template T | |
* @typedef UnionFuns | |
* @type {Object} | |
* | |
* @prop {() => string} toString | |
* Returns a stringified version of the type. | |
* | |
* @prop {(constructor: ConcreteUnion<T>) => boolean} is | |
* Checks if the value's tipe matches the given constructor | |
*/ | |
/** | |
* The function used to instantiate a concrete union type. | |
* | |
* @template T | |
* @callback UnionConstructor | |
* @param {...any} [args] | |
* @returns {ConcreteUnion<T>} | |
*/ | |
/** | |
* @template T | |
* @typedef ConcreteUnion | |
* @type {UnionFuns<T> & UnionProtoFuns<T>} | |
*/ | |
/** | |
* @template T | |
* @typedef UnionType | |
* @type {UnionConstructor<T> & ConcreteUnion<T>} | |
*/ | |
/** | |
* @template T | |
* @typedef {{[K in keyof T]: UnionType<T>} & UnionFuns<T>} Union | |
*/ | |
/** | |
* Returns Type Representative containing constructors of for each key in | |
* constructors as a property. | |
* | |
* @template T | |
* @param {string} name | |
* @param {T} constructors | |
* @returns {Union<T>} | |
*/ | |
export default function union(name, constructors) { | |
let tagged = taggedSum(name, constructors); | |
tagged.prototype.match = function(clauses) { return this.cata(clauses)} | |
/** | |
* Typescript is not powerful enough to understand the tricks daggy uses | |
* to construct the tagged sum, so I need it to ignore the return value | |
* to make it use the above type declarations instead. | |
*/ | |
// @ts-ignore | |
return tagged; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment