I would like to construct a Maybe a
type in typescript a la Haskell:
data Maybe a = Just a | Nothing
It seems like the way to do it in typescript
is:
interface Nothing { tag "Nothing }
type Maybe<T> = T | Nothing
I would like to make a function:
function foo(x : string) : Maybe<T> {
return Nothing
}
akin to:
foo : String -> Maybe a
foo _ = Nothing
However this does not work in typescript
. What is the right way to return a value Nothing
in typescript? I would like to avoid using null
if possible.
___________________________________________________-
Edit: It would be really nice if the function foo
would return a value Nothing
because I would like to pattern match on value constructor later, ie:
case blah blah of
| Just x -> x + x
| Nothing -> "no words"
Depending on the case, it can be void
, undefined
, or ?
optional modifier for properties and parameters.
It's:
function foo(x : string) : number | void {
// returns nothing
}
void
and undefined
types are compatible, but there is some difference between them. The former is preferable for function return types, because the latter requires a function to have return
statement:
function foo(x : string) : number | undefined {
return;
}
Maybe
can be implemented with generic type. Explicit Nothing
type can be implemented with unique symbol:
const Nothing = Symbol('nothing');
type Nothing = typeof Nothing;
type Maybe<T> = T | Nothing;
function foo(x : string) : Maybe<number> {
return Nothing;
}
Or a class (private fields can be used to prevent ducktyping):
abstract class Nothing {
private tag = 'nothing'
}
type Maybe<T> = T | typeof Nothing;
function foo(x : string) : Maybe<number> {
return Nothing;
}
Notice that class types designate class instance type and require to use typeof
when a class is referred.
Or an object (if duck typing can be desirable):
const Nothing: { tag: 'Nothing' } = { tag: 'Nothing' };
type Nothing = typeof Nothing;
type Maybe<T> = T | Nothing;
function foo(x : string) : Maybe<number> {
return Nothing;
}
enum Nothing {
Nothing = 'Nothing'
}
type Maybe<A> = A | Nothing
type MaybeNumber = Maybe<number>
const isNothing = (m: MaybeNumber): m is Nothing => m === Nothing.Nothing
const foo = (m: MaybeNumber) => {
if (isNothing(m)) {
return Nothing.Nothing
} else {
return m + 1
}
}
foo(1) // ✔️
foo('Nothing') // ❌
foo(Nothing.Nothing) // ✔️
or without using an enum
using a string literal type
const Nothing = '__nothing__'
type Maybe<A> = A | Nothing
type MaybeNumber = Maybe<number>
const isNothing = (m: MaybeNumber): m is Nothing => m === Nothing
const foo = (m: MaybeNumber) => {
if (isNothing(m)) {
return Nothing
} else {
return m + 1
}
}
foo(1) // ✔️
foo(Nothing) // ✔️
foo('__nothing__') // ✔️
or with symbols
const nothing = Symbol('Nothing')
type Nothing = typeof nothing
type Maybe<A> = A | Nothing
type MaybeNumber = Maybe<number>
const isNothing = (m: MaybeNumber): m is Nothing => m.toString() === 'Symbol(Nothing)'
const foo = (m: MaybeNumber) => {
if (isNothing(m)) {
return nothing
} else {
return m + 1
}
}
foo(1) // ✔️
foo(nothing) // ✔️
live on stackblitz
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With