Skip to content

Instantly share code, notes, and snippets.

@uniphil
Created September 22, 2015 12:13
Show Gist options
  • Save uniphil/36c5d750fd7a1cf704a5 to your computer and use it in GitHub Desktop.
Save uniphil/36c5d750fd7a1cf704a5 to your computer and use it in GitHub Desktop.
Type-checked hard-coded results match API
interface Matchable {
name: string;
data: any[];
match: (options: ProjectActionMatcher) => any;
}
interface MatcherCatchAll {
_: (...args: any[]) => any;
}
interface ProjectActionMatcher {
Reset: () => any;
Select: (id: string) => any;
Hover: (id: string) => any;
Rename: (id: string, newName: string) => any;
}
function hasProp(obj: any, prop: string): void {
}
class ProjectActionClass {
static options = {Reset: 1, Select: 1, Hover: 1, Rename: 1};
name;
data;
constructor(...args: any[]) {
this.data = args;
}
match(options: ProjectActionMatcher|MatcherCatchAll) {
if (!options.hasOwnProperty('_')) { // no _: not catch-all, already typechecked
return options[this.name](...this.data);
} else { // has a catch-all, so lets first check all the keys
for (const o in options) {
if (options.hasOwnProperty(o) && o !== '_') {
if (typeof options[o] !== 'function') {
// Every value of `options` must be a function
throw new Error(`options value for option ${o} is a ` +
`${typeof options[o]} but should be a function`);
}
}
}
if (typeof options[this.name] === 'undefined') {
// not us, do the catch-all
return (<MatcherCatchAll>options)._(this);
} else {
return options[this.name](...this.data);
}
}
}
}
class ResetOption extends ProjectActionClass { name = 'Reset'; }
class SelectOption extends ProjectActionClass { name = 'Select'; }
class HoverOption extends ProjectActionClass { name = 'Hover'; }
class RenameOption extends ProjectActionClass { name = 'Rename'; }
const ProjectAction = {
Reset: () => new ResetOption(),
Select: (id) => new SelectOption(id),
Hover: (id) => new HoverOption(id),
Rename: (id, newName) => new RenameOption(id, newName),
};
ProjectAction.Reset().match({
Reset: () => console.log(`Reset`),
Select: (id) => console.log(`Select: ${id}`),
Hover: (id) => console.log(`Hover: ${id}`),
Rename: (id, newName) => console.log(`Rename: ${id} ${newName}`),
});
// should fail: bad option arg:
ProjectAction.Reset('z').match({
Reset: () => console.log(`Reset`),
Select: (id) => console.log(`Select: ${id}`),
Hover: (id) => console.log(`Hover: ${id}`),
Rename: (id, newName) => console.log(`Rename: ${id} ${newName}`),
});
// // Should fail: missing option:
// ProjectAction.Reset().match({
// Reset: () => console.log(`Reset`),
// Select: (id) => console.log(`Select: ${id}`),
// Hover: (id) => console.log(`Hover: ${id}`),
// });
ProjectAction.Select('sup').match({
Reset: () => console.log(`Reset`),
Select: (id) => console.log(`Select: ${id}`),
Hover: (id) => console.log(`Hover: ${id}`),
Rename: (id, newName) => console.log(`Rename: ${id} ${newName}`),
});
// // should fail: missing option arg:
// ProjectAction.Select().match({
// Reset: () => console.log(`Reset`),
// Select: (id) => console.log(`Select: ${id}`),
// Hover: (id) => console.log(`Hover: ${id}`),
// Rename: (id, newName) => console.log(`Rename: ${id} ${newName}`),
// });
ProjectAction.Reset().match({
Reset: () => console.log(`Reset (not wild)`),
_: (option) => console.log(`wild!: ${option.data}`),
});
ProjectAction.Rename('z', 'something else').match({
Reset: () => console.log(`Reset (not wild)`),
_: (option) => console.log(`wild! ${option.name} with ${option.data}`),
});
// // should fail at runtime: non-function branch
// ProjectAction.Rename('z', 'something else').match({
// Reset: null,
// _: (option) => console.log(`should not get here`),
// });
// // should fail at compile time: bad option name (wooooo union types!)
// ProjectAction.Rename('z', 'something else').match({
// BadOptionName: () => null,
// _: (option) => console.log(`should not get here`),
// });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment