Skip to content

Instantly share code, notes, and snippets.

View ksaldana1's full-sized avatar

Kevin Saldaña ksaldana1

View GitHub Profile

Domain Modeling with Tagged Unions in GraphQL, ReasonML, and TypeScript

GraphQL has exploded in popularity since its open-source announcement in 2015. For developers who had spent a lot of time managing data transformations from their back-end infrastructure to match front-end product needs, GraphQL felt like a tremendous step forwards. Gone were the days of hand-writing BFFs to manage problems of over-fetching.

A lot of value proposition arguments around GraphQL have been about over/under fetching, getting the data shape you ask for, etc. But I think GraphQL provides us more than that—it gives us an opportunity to raise the level of abstraction of our domain, and by doing so allow us to write more robust applications that accurately model the problems we face in the real world (changing requirements, one-off issues).

An underappreciated feature of GraphQL is its type system, and in particular features like [union types](https:

// 1) Types that have a common, singleton type property — the discriminant.
// In this example the "kind" property is the discriminant.
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
@ksaldana1
ksaldana1 / easymotion.json
Created August 23, 2019 16:54
easymotion settings in vscode
{
"vim.easymotion": true,
"vim.easymotionMarkerHeight": 20,
"vim.easymotionMarkerWidthPerChar": 12,
"vim.easymotionMarkerFontSize": "16",
"vim.easymotionMarkerBackgroundColor": "#428aff",
"vim.easymotionMarkerForegroundColorOneChar": "#fffcfc",
"vim.easymotionMarkerForegroundColorTwoChar": "#fffcfc",
"vim.easymotionMarkerFontFamily": "Arial",
"vim.normalModeKeyBindingsNonRecursive": [
import { EventObject } from 'xstate';
interface StateSchema<TEvents extends EventObject, TAllStates extends string> {
states?: { [K in TAllStates]: StateNode<TEvents, TAllStates> };
}
interface StateNode<
TEvents extends EventObject,
TAllStates extends string,
TContext = any
> extends StateSchema<TEvents, TAllStates> {
@ksaldana1
ksaldana1 / example.ts
Last active January 30, 2019 16:28
example
import { protos } from 'my_company_protos'
export type User = protos.user.User;
@ksaldana1
ksaldana1 / kind-printer.ts
Created January 30, 2019 06:16
kind-printer
ts.forEachChild(source, node => {
if (ts.isTypeAliasDeclaration(node)) {
console.log(node.kind);
}
})
// prints TypeAliasDeclaration
ts.forEachChild(source, node => {
if (ts.isTypeAliasDeclaration(node)) {
const symbol = checker.getSymbolAtLocation(node.name);
const type = checker.getDeclaredTypeOfSymbol(symbol);
const properties = checker.getPropertiesOfType(type);
properties.forEach(declaration => {
console.log(declaration.name);
// prints username, info
});
}
@ksaldana1
ksaldana1 / aliasTransformer.ts
Last active May 6, 2019 07:20
aliasTransformer
import path from 'path';
import ts from 'typescript';
import _ from 'lodash';
import fs from 'fs';
const filePath = path.resolve(_.first(process.argv.slice(2)));
const program = ts.createProgram([filePath], {});
const checker = program.getTypeChecker();
const source = program.getSourceFile(filePath);
@ksaldana1
ksaldana1 / input-output.ts
Last active January 30, 2019 05:39
transform-input-output
// input
export namespace protos { // ModuleDeclaration
export namespace user { // ModuleDeclaration
// Module Block
export interface User { // InterfaceDeclaration
username: string; // username: string is PropertySignature
info: protos.Info.User; // TypeReference
}
}
export namespace Info {
@ksaldana1
ksaldana1 / number-transformer.ts
Last active January 30, 2019 05:18
number-transformer
const source = `
const two = 2;
const four = 4;
`;
function numberTransformer<T extends ts.Node>(): ts.TransformerFactory<T> {
return context => {
const visit: ts.Visitor = node => {
if (ts.isNumericLiteral(node)) {
return ts.createStringLiteral(node.text);