Skip to content

Instantly share code, notes, and snippets.

@suchipi
Created December 16, 2018 04:42
Show Gist options
  • Save suchipi/18473367e755907497282f62b0fca269 to your computer and use it in GitHub Desktop.
Save suchipi/18473367e755907497282f62b0fca269 to your computer and use it in GitHub Desktop.
Tagged Unions in Flow
// @flow
type NoneType = {|
type: 'None',
|};
type AppleType<AppleInner> = {|
type: 'Apple',
value: AppleInner,
|};
type BananaType = {|
type: 'Banana',
value: number,
|};
type DisjointUnion<AppleInner> = NoneType | AppleType<AppleInner> | BananaType;
export default class MaybeFruit<AppleInner> {
type: $PropertyType<DisjointUnion<AppleInner>, 'type'>;
value: $PropertyType<DisjointUnion<AppleInner>, 'value'>;
constructor(data: DisjointUnion<AppleInner>) {
Object.assign(this, data);
}
static None: MaybeFruit<any> = new MaybeFruit({
type: 'None',
});
static Apple = <OtherAppleInner>(
value: OtherAppleInner
): MaybeFruit<OtherAppleInner> =>
new MaybeFruit({
type: 'Apple',
value,
});
static Banana = (value: number): MaybeFruit<any> =>
new MaybeFruit({type: 'Banana', value});
match<NoneResult, AppleResult, BananaResult, DefaultResult>(
// You need to specify either a handler for every case,
// or a default handler and a handler for some cases.
matchObj:
| {|
None: () => NoneResult,
Apple: (value: AppleInner) => AppleResult,
Banana: (value: number) => BananaResult,
|}
| {|
None?: () => NoneResult,
Apple?: (value: AppleInner) => AppleResult,
Banana?: (value: number) => BananaResult,
_: (...args: Array<mixed>) => DefaultResult,
|}
): NoneResult | AppleResult | BananaResult | DefaultResult {
const self = ((this: any): DisjointUnion<AppleInner>);
if (self.type === 'None') {
return (matchObj.None || matchObj._)();
} else if (self.type === 'Apple') {
return (matchObj.Apple || matchObj._)(self.value);
} else if (self.type === 'Banana') {
return (matchObj.Banana || matchObj._)(self.value);
} else {
(self: empty);
}
}
}
function analyzeFruit<AppleInner>(fruit: MaybeFruit<AppleInner>) {
const phrase1 = fruit.match({
Apple(inner) {
return 'it was an apple with inner: ' + String(inner);
},
_() {
return 'idk';
},
});
const phrase2 = fruit.match({
None() {
return 'no fruit :(';
},
Apple() {
return 'yup still an apple';
},
Banana(num) {
return 'it was a banana with number: ' + num;
},
});
return [phrase1, phrase2];
}
analyzeFruit(MaybeFruit.Apple('yellow')); // ['it was an apple with inner: yellow', 'yup still an apple']
analyzeFruit(MaybeFruit.None); // ['idk', 'no fruit :(']
analyzeFruit(MaybeFruit.Banana(5)); // ['idk', 'it was a banana with number: 5']
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment