Skip to content

Instantly share code, notes, and snippets.

@bcherny
Last active February 18, 2023 10:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bcherny/f6fb545627f449f18cd05fda5e31a8ea to your computer and use it in GitHub Desktop.
Save bcherny/f6fb545627f449f18cd05fda5e31a8ea to your computer and use it in GitHub Desktop.
Options in TypeScript!
// @see http://www.scala-lang.org/api/2.7.4/scala/Option.html
class Opt<A> {
value: A | null
// TODO: use constructor fn instead
static from<A>(value: A | null): Opt<A> {
return value == null ? None : Some(value)
}
get(): A {
if (this.isDefined()) return this.value
throw new Error('Opt has no value')
}
getOrElse(a: A): A { return this.isDefined() ? this.get() : a }
isDefined(): this is Some<A> { return this.value !== null }
isEmpty() { return !this.isDefined() }
orElse(a: Opt<A>): Opt<A> { return this.isDefined() ? this : a }
toList(): A[] { return this.isDefined() ? [this.value] : [] }
// toLeft
// toRight
// filter
// flatMap
map<B>(fn: (a: A) => B): Opt<B> {
return this.isDefined() ? Some(fn(this.value)) : None
}
}
interface Some<A> extends Opt<A> {
value: A
get(): A
isDefined(): true
isEmpty(): false
}
interface None<A> extends Opt<A> {
value: null
get(): never
isDefined(): false
isEmpty(): true
}
const Some = <A>(a: A): Some<A> => Object.create(Opt, {
value: { value: a }
})
const None: None<any> = Object.create(Opt, {
value: { value: null }
})
/////////////// patch stdlib
interface Array<T> {
find(predicate: (value: T, index: number, obj: Array<T>) => boolean, thisArg?: any): Opt<T>
pop(): Opt<T>
shift(): Opt<T>
}
interface Map<K, V> {
get(key: K): Opt<V>
}
interface String {
codePointAt(pos: number): Opt<number>
}
// TODO
// interface SymbolConstructor {
// keyFor(sym: symbol): Opt<string>
// }
interface WeakMap<K, V> {
get(key: K): Opt<V>
}
const methods: {[Type: string]: string[]} = {
Array: ['find', 'pop', 'shift'],
Map: ['get'],
String: ['codePointAt'],
WeakMap: ['get']
}
for (let Type in methods) {
methods[Type].forEach(method => {
const originalMethod = window[Type].prototype[method]
window[Type].prototype[method] = function<T>(): Opt<T> {
return Opt.from(originalMethod.apply(this, arguments))
}
})
}
///////////////// examples
const a = Some(42)
const b = a.get
const c = a.getOrElse(100)
const d = a.toList
const e = [1, 2, 3].find(_ => _ == 2).getOrElse(4)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment