Published
Edited
Sep 8, 2020
Importers
Insert cell
Insert cell
Insert cell
orderType = t.union(
[
t.type(
{
toppings: t.array(literalUnion(['pineapple', 'bacon', 'jalapeño'])),
size: literalUnion(['sm', 'md', 'lg']),
sauce: t.literal('red') // There is only one valid option on a pizza.
},
'pizza'
),
t.type(
{
noodles: literalUnion(['spaghetti', 'linguine', 'fettuccine']),
size: literalUnion(['sm', 'md', 'lg']),
sauce: literalUnion(['marinara', 'arrabiata', 'pesto'])
},
'pasta'
)
],
'order'
)
Insert cell
Insert cell
orderType.decode({
toppings: ['bacon', 'pineapple', 'jalapeño'],
size: 'lg',
sauce: 'red'
})
Insert cell
Insert cell
result = orderType.decode({
toppings: ['bacon', 'pineapple', 'jalapeño', 'anchovies'],
size: 'xl',
sauce: 'white'
})
Insert cell
Insert cell
result.left[0]
Insert cell
Insert cell
Insert cell
Insert cell
tree = getTree(result)
Insert cell
Insert cell
drawTree(tree.toJS())
Insert cell
Insert cell
function printExplanation(tree, visitorFn) {
const { markdown, includeChildren = true } = visitorFn(tree);
const children = includeChildren ? tree.getChildren() : [];
return md`
- ${markdown}
${children.map(child => printExplanation(child, visitorFn))}
`;
}
Insert cell
Insert cell
printExplanation(tree, tree => {
const actual = stringify(tree.context.actual);
const type = tree.context.type.name;

return {
markdown: `Type \`${actual}\` is not assignable to type \`${type}\``
};
})
Insert cell
Insert cell
printExplanation(tree, tree => {
const actual = stringify(tree.context.actual);
const type = tree.context.type.name;

return {
markdown: `Type \`${actual}\` is not assignable to type \`${type}\``,
includeChildren: !isLiteralUnion(tree)
};
})
Insert cell
Insert cell
missingPropertyVisitor = tree => {
const actual = tree.context.actual;
const type = tree.context.type.name;
const key = tree.context.key;
const message = tree.error && tree.error.message;

const parent = tree.getParent();
const parentActual = parent && parent.context.actual;
const parentName = parent && parent.context.type.name;
const isMissingProperty =
actual === undefined && parent && !(key in parentActual);

const markdown = message
? message
: isMissingProperty
? `Property \`${key}\` is missing in type \`${stringify(
parentActual
)}\` but required in type \`${parentName}\``
: `Type \`${stringify(actual)}\` is not assignable to type \`${type}\``;

return {
markdown,
includeChildren: !isLiteralUnion(tree)
};
}
Insert cell
Insert cell
printExplanation(tree, missingPropertyVisitor)
Insert cell
Insert cell
printExplanation(
getTree(
orderType.decode({
toppings: ['bacon', 'pineapple', 'jalapeño', 'anchovies'],
size: 'xl',
sauce: 'white',
noodles: undefined // <-- the property is present, but its value is undefined
})
),
missingPropertyVisitor
)
Insert cell
Insert cell
class ErrorTree {
constructor(parent, context) {
this.parent = parent;
this.context = context;
this.children = {};
this.error = null;
}

getParent() {
return this.parent;
}

getChildren() {
return Object.values(this.children);
}

name() {
return this.error ? getMessage(this.error) : this.context.type.name;
}

toJS() {
const children = this.getChildren().map(child => child.toJS());

return {
name: this.name(),
children
};
}

addError(error) {
const ctx = [...error.context];
this.context = ctx.shift();
this.addError_internal(ctx, error);
}

addError_internal(ctx, error) {
const currContext = ctx.shift();
if (currContext) {
const key = currContext.key;

if (!this.children[key]) {
this.children[key] = new ErrorTree(this, currContext);
}

this.children[key].addError_internal(ctx, error);
} else {
this.error = error;
}
}
}
Insert cell
function fromErrors(errors) {
const tree = new ErrorTree();
errors.forEach(error => {
tree.addError(error);
});
return tree;
}
Insert cell
function stringify(v) {
if (typeof v === 'function') {
return getFunctionName(v);
}

if (typeof v === 'number' && !isFinite(v)) {
if (isNaN(v)) {
return 'NaN';
}
return v > 0 ? 'Infinity' : '-Infinity';
}

return JSON.stringify(v);
}
Insert cell
function shiftContextKeys(context) {
const shifted = [];
for (let i = 0; i < context.length; i++) {
const next = context[i + 1];
shifted.push({ ...context[i], key: next ? next.key : undefined });
}
return shifted;
}
Insert cell
function getContextPath(context) {
return shiftContextKeys(context)
.filter(({ type }) => !(type instanceof t.UnionType))
.filter(({ key }) => Boolean(key))
.map(({ type, key }) =>
type instanceof t.ArrayType ? `[${key}]` : `.${key}`
)
.join('');
}
Insert cell
function getMessage(e) {
return e.message !== undefined
? e.message
: `${stringify(e.value)} → ${getContextPath(e.context)}`;
}
Insert cell
getFunctionName = f => f.displayName || f.name || `<function${f.length}>`
Insert cell
literalUnion = args => t.union(args.map(a => t.literal(a)))
Insert cell
isLiteralUnion = tree =>
tree.context.type instanceof t.UnionType &&
tree.getChildren().every(child => child.context.type instanceof t.LiteralType)
Insert cell
Insert cell
getTree = v => {
return fp.function.pipe(
v,
fp.either.fold(fromErrors, () => ['No errors'])
);
}
Insert cell
PathReporter = {
function stringify(v) {
if (typeof v === 'function') {
return getFunctionName(v);
}
if (typeof v === 'number' && !isFinite(v)) {
if (isNaN(v)) {
return 'NaN';
}
return v > 0 ? 'Infinity' : '-Infinity';
}
return JSON.stringify(v);
}
function getContextPath(context) {
return context
.map(function(_a) {
var key = _a.key,
type = _a.type;
return key + ": " + type.name;
})
.join('/');
}
function getMessage(e) {
return e.message !== undefined
? e.message
: "Invalid value " +
stringify(e.value) +
" supplied to " +
getContextPath(e.context);
}
function failure(es) {
return es.map(getMessage);
}
function success() {
return ['No errors!'];
}
return {
report: fp.either.fold(failure, success)
};
}
Insert cell
Insert cell
fp = import('https://unpkg.com/fp-ts@2.8.2/es6/index.js?module')
Insert cell
d3 = require('d3@6')
Insert cell
t = import('https://unpkg.com/io-ts@2.2.10/es6/index.js?module')
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more