Closed
Description
I'm not sure if this is a bug or a suggestion as the documentation only talks about types (as opposed to classes) and I can understand not permitting discriminated unions, also known as tagged unions or algebraic data types that involve classes. I am coming from Scala which supports it, but each language is different and I guess you may be limited by Javascript. Here's some code:
TypeScript Version: 2.6.0
Code
Maybe.ts
export class Some<A> {
readonly kind: "some";
readonly value: A;
constructor(value: A) {
this.value = value;
}
then<B>(callback: (a: A) => B): Maybe<B> {
return new Some<B>(callback(this.value)); // todo factory
}
}
export class None<A> {
readonly kind: "none";
constructor() { }
then<B>(callback: (a: A) => B): Maybe<B> {
return new None<B>();
}
}
export type Maybe<A> = Some<A> | None<A>;
export function print<A>(m: Maybe<A>) {
switch (m.kind) {
case "some": return `Some(${m.value})`;
case "none": return "None";
}
}
Maybe.spec.ts
import { print, Maybe, Some, None } from "./Maybe";
import "mocha";
import { expect } from "chai";
describe("maybe", () => {
it("should permit exhaustive checking", () => {
expect(print(new Some(10))).to.equal("Some(10)");
expect(print(new None<number>())).to.equal("None");
});
});
Expected behavior:
Test passes
maybe
✓ should permit exhaustive checking
Actual behavior:
Test fails
1) maybe should permit exhaustive checking:
AssertionError: expected undefined to equal 'Some(10)'
at Context.it (lib/Maybe.spec.js:8:63)
Work around code
Maybe.ts
export interface Some<A> {
readonly kind: "some";
readonly value: A;
}
export interface None {
readonly kind: "none";
}
export type Maybe<A> = Some<A> | None;
export function print<A>(m: Maybe<A>) {
switch (m.kind) {
case "some": return `Some(${m.value})`;
case "none": return "None";
}
}
export function maybe<A>(a: A | null | undefined): Maybe<A> {
if (a == null || a == undefined) {
return { kind: "none" };
} else {
return { kind: "some", value: a};
}
}
// todo then<A, B>(a: Maybe<A>, map: A => B): Maybe<B>
Maybe.spec.ts
import { print, Maybe, maybe } from "./Maybe";
import "mocha";
import { expect } from "chai";
describe("maybe", () => {
it("should permit exhaustive checking", () => {
expect(print(maybe(10))).to.equal("Some(10)");
expect(print(maybe<number>(null))).to.equal("None");
});
});
Thanks for the fun, manageable language.