Skip to content

Instantly share code, notes, and snippets.

@wkronemeijer
Last active June 18, 2016 17:53
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 wkronemeijer/ca9d0f660c44624364ad4b690a01db17 to your computer and use it in GitHub Desktop.
Save wkronemeijer/ca9d0f660c44624364ad4b690a01db17 to your computer and use it in GitHub Desktop.
ES6 Module for creating what can be considered **almost** ADTs.
export interface InstanceConstructor<
/**Enum */ E,
/**Shared properties */ S,
/**Unique properties */ T
> {
/**Create new instance */
(diffs?: S & T): S & T;
/**Type guard for destructuring */
typecheck(object: {}): object is S & T;
/**For introspection, and to make IntelliSense work */
Kind: E;
}
export interface CaseConstructor<E, S> {
/**Use `Instance` to create a type alias for the enum */
Instance: S;
/**Create a new case for the `Enum` */
Case<T>(member: E, template: T): InstanceConstructor<E, S, T>;
}
const _EnumSymbol = Symbol('enum');
const _CaseSymbol = Symbol('case');
export function Enum<E, S>(
/**Host `enum` for names and IDs */ enum_: {},
/**`enum` member to help IntelliSense */ any_member: E,
/**Properties common to all `Case`s */ common_properties: S
): CaseConstructor<E, S> {
return {
Instance: common_properties,
Case<T>(member: E, template: T): InstanceConstructor<E, S, T> {
template[_EnumSymbol] = enum_;
template[_CaseSymbol] = +member;
template[Symbol.toStringTag] = enum_[+member] as string;
const create = (diffs?: S & T) => Object.assign({}, common_properties, template, diffs);
const helper = {
Kind: member,
typecheck(object: {}): object is S & T {
return (
template[_EnumSymbol] === object[_EnumSymbol] &&
template[_CaseSymbol] === object[_CaseSymbol]
);
},
};
return Object.assign(create, helper);
},
};
}
@wkronemeijer
Copy link
Author

How to use:

  1. Create an enum to give names to the cases (and to help IntelliSense)
  2. Create the ADT by using Enum, and pass it the helper enum, any member (to help IntelliSense) and an object containing any shared properties between all cases.
  3. Add all individual cases by using the .Case method on the Enum, and pass it a unique enum member, and any properties specific to that case.

@wkronemeijer
Copy link
Author

wkronemeijer commented May 17, 2016

Example:

// Step 1
enum ColorKind {
    RGBA, HSLA,

    /* @internal */
    _Self = -1
}

// Step 2
let Color = Enum(ColorKind, ColorKind._Self, {
    alpha: 0,
});
type Color = typeof Color.Instance;

// Step 3
let RGBA = Color.Case(ColorKind.RGBA, {
    red: 0, green: 0, blue: 0,
});

let HSLA = Color.Case(ColorKind.HSLA, {
    hue: 0, saturation: 0, lightness: 0,
});


let electric_seafoam = RGBA({
    red: 37, 
    green: 231,
    blue: 199, 
    alpha: 1,
});

let electric_salmon = HSLA({
    hue: 6,
    saturation: 93 / 100,
    lightness: 58 / 100, 
    alpha: 1,
});


for (let color of [electric_salmon, electric_seafoam]) {
    if (HSLA.typecheck(color)) {
        console.log(color.hue);
    } else if (RGBA.typecheck(color)) {
        console.log(color.red);
    }

    console.log(color.alpha);
}

@wkronemeijer
Copy link
Author

Why use this: your ADTs typecheck and you can use IntelliSense on instance members

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment