Skip to content

Instantly share code, notes, and snippets.

@moosch
Last active November 26, 2019 09:24
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 moosch/675a9a70ea02117a76de634371eba96c to your computer and use it in GitHub Desktop.
Save moosch/675a9a70ea02117a76de634371eba96c to your computer and use it in GitHub Desktop.
/**
* A demonstration of building a Type Class in JavaScript with a custom DataType.
*
* This is a simple container that has three defined properties.
* The type methods are functional, mutating none of it's contents.
*/
const typeGuard = (ExpectedType, data) => {
if (!ExpectedType) {
throw new ReferenceError('Expected a Type as first argument');
}
if (
data === undefined
|| data === null
|| !(data instanceof ExpectedType)
) {
let datatype;
switch (true) {
case (data === undefined):
datatype = undefined;
break;
case (data === null):
datatype = null;
break;
default:
datatype = data.constructor ? data.constructor.name : data;
}
throw new TypeError(`${datatype} is not of type ${ExpectedType.name}`);
}
};
const _boxShake = (datatype) => {
typeGuard(Box, datatype);
console.log(`Box(...)`);
return datatype;
};
const _boxPeek = (datatype) => {
typeGuard(Box, datatype);
console.log(`Box(${datatype.valueOf()})`);
return datatype;
};
const _boxAdd = (data, val) => {
typeGuard(Box, data);
if (isNaN(val) && typeof val !== 'string') {
throw new TypeError(`Expected either a string or number type but got ${typeof val}`);
}
return Box(`${data.valueOf()}${val}`);
};
const _boxOpen = (datatype) => {
typeGuard(Box, datatype);
return datatype.valueOf();
};
const Box = (x) => ({
__typeclass: 'Box',
valueOf: () => x,
shake: () => _boxShake(Box(x)),
peek: () => _boxPeek(Box(x)),
add: (val) => _boxAdd(Box(x), val),
open: () => _boxOpen(Box(x)),
});
Object.defineProperties(Box, {
shake: { value: _boxShake },
peek: { value: _boxPeek },
add: { value: _boxAdd },
open: { value: _boxOpen },
});
Object.defineProperty(Box, Symbol.hasInstance, {
value: (instance) => instance['__typeclass'] === 'Box',
});
let box = Box('🤫');
(box instanceof Box) // => true
box.shake(); // => Box(...)
box.peek(); // => Box(🤫)
box.open(); // => '🤫'
Box.shake(box); // => Box(...)
Box.peek(box); // => Box(🤫)
Box.open(box); // => '🤫'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment