# Technical overview
## A basic `Option` type
```ts
// Option.ts
// definition
export class None {
readonly tag: 'None' = 'None'
}
export class Some {
readonly tag: 'Some' = 'Some'
constructor(readonly value: A) {}
}
export type Option = None | Some
// helpers
export const none: Option = new None()
export const some = (a: A): Option => {
return new Some(a)
}
// a specialised map for Option
const map = (f: (a: A) => B, fa: Option): Option => {
switch (fa.tag) {
case 'None':
return fa
case 'Some':
return some(f(fa.value))
}
}
```
Usage
```ts
const double = (n: number): number => n * 2
const len = (s: string): number => s.length
console.log(map(double, some(1))) // { tag: 'Some', value: 2 }
console.log(map(double, none)) // { tag: 'None' }
console.log(map(len, some(2))) // <= static error: Type 'number' is not assignable to type 'string'
```
## Adding static land support
TypeScript doesn't support higher kinded types
```ts
interface Functor {
map: (f: (a: A) => B, fa: ?) => ?
}
```
but we can fake them with an interface
```ts
// HKT.ts
export interface HKT {
_URI: F
_A: A
}
```
where `F` is a unique identifier representing the type constructor and `A` its type parameter.
Now we can define a generic `Functor` interface
```ts
// Functor.ts
import { HKT } from './HKT'
export interface Functor {
map: (f: (a: A) => B, fa: HKT) => HKT
}
```
and redefine the `Option` type
```ts
// Option.ts
// unique identifier
export const URI = 'Option'
export type URI = typeof URI
export class None {
readonly _URI!: URI
readonly _A!: never
readonly tag: 'None' = 'None'
}
export class Some {
readonly _URI!: URI
readonly _A!: A
readonly tag: 'Some' = 'Some'
constructor(readonly value: A) {}
}
export type Option = None | Some
export const none: Option = new None()
export const some = (a: A): Option => {
return new Some(a)
}
const map = (f: (a: A) => B, fa: Option): Option => {
switch (fa.tag) {
case 'None':
return fa
case 'Some':
return some(f(fa.value))
}
}
```
Let's define an instance of `Functor` for `Option`
```ts
// static land Functor instance
export const option: Functor = {
map
}
```
There's a problem though, this code doesn't type-check with the following error
```
Type 'HKT<"Option", A>' is not assignable to type 'Option'
```
Every `Option` is a `HKT<"Option", A>` but the converse is not true. In order to fix this (we **know** that `Option = HKT<"Option", A>`) functions like `map` should accept the more general version `HKT<"Option", A>` and return the more specific version `Option`
```ts
const map = (f: (a: A) => B, hfa: HKT): Option => {
const fa = hfa as Option
switch (fa.tag) {
case 'None':
return fa
case 'Some':
return some(f(fa.value))
}
}
export const option: Functor = {
map // no error
}
```
There's another issue though: when trying to use the instance we don't get an `Option` as a result
```ts
// x: HKT<"Option", number>
const x = option.map(double, some(1))
```
we get an `HKT<"Option", number>`.
We must somehow teach TypeScript that `HKT<"Option", number>` is really `Option`, or more generally that
`HKT<"Option", A>` is `Option` for all `A`.
We'll use a feature called [Module Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) for that.
Let's move the `HKT` definition to its own file and add a type-level map named `URI2HKT`
```ts
// HKT.ts
export interface HKT {
_URI: F
_A: A
}
// type-level map, maps a URI to its corresponding type
export interface URI2HKT {}
```
Let's add some helpers types
```ts
// all URIs
export type URIS = keyof URI2HKT
// given a URI and a type, extracts the corresponding type
export type Type = URI2HKT[URI]
```
Adding an entry to the type-level map `URI2HKT` means to leverage the module augmentation feature
```ts
// Option.ts
declare module './HKT' {
interface URI2HKT {
Option: Option // maps the type literal "Option" to the type `Option`
}
}
```
Now we can redefine `Functor` in order to leverage this type-level machinery
```ts
// Functor.ts
import { URIS, Type } from './HKT'
export interface Functor1 {
map: (f: (a: A) => B, fa: Type) => Type
}
```
and fix the instance definition
```ts
// Option.ts
import { Functor1 } from './Functor'
const map = (f: (a: A) => B, fa: Option): Option => {
switch (fa.tag) {
case 'None':
return fa
case 'Some':
return some(f(fa.value))
}
}
export const option: Functor1 = {
map
}
// x: Option
const x = option.map(double, some(1))
```
## Adding fantasy land support
Let's add a `map` method to `None` and `Some`
```ts
// Option.ts
export class None {
readonly _URI!: URI
readonly _A!: never
readonly tag: 'None' = 'None'
map(f: (a: A) => B): Option {
return none
}
}
export class Some {
readonly _URI!: URI
readonly _A!: A
readonly tag: 'Some' = 'Some'
constructor(readonly value: A) {}
map(f: (a: A) => B): Option {
return some(f(this.value))
}
}
export type Option = None | Some
```
Note that `None` has a type parameter now, because the signature of `map` (the method) must be the same for both `None` and `Some` otherwise TypeScript will complain.
The implementation of `map` (the static function) is now trivial.
```ts
const map = (f: (a: A) => B, fa: Option) => {
return fa.map(f)
}
```
We can now use a nice chainable API (a kind of do notation)
```ts
// x: Option
const x = some('foo')
.map(len)
.map(double)
```