In fp-ts they have this workaround for higher kinded types:
export interface HKT<URI, A> {
readonly _URI: URI;
readonly _A: A;
}
And it can be used like this:
export interface Foldable<F> {
readonly URI: F;
reduce: <A, B>(fa: HKT<F, A>, b: B, f: (b: B, a: A) => B) => B;
}
What are the members _URI
and what is _A
?
This article written by the author of fp-ts
explains very well,in combination with documentation on TypeScript's union types.
_A
is the type of the value in our HKT: HKT<"Option", string> => Option<string>
. We'll see how mapping works in a bit.
_URI
is the identifier for the HKT, for example Option.ts. It has 2 instances, None
and Some
, both of which have "Option"
as their _URI
.
Taking a look at the usage of F
in Foldable
we can see it's used in the Type
type, along with A
; Type<F, A>
. Assume F == "Option" & A == string
so Type<F, A> == Type<"Option", string>
, which is very similar to Option<string>
. Currently, we cannot pass a generic type to another generic as it's generic parameter: <F,A>(fa: F<A>)
will not compile and this is the main problem.
To achieve Type<"Option", string> => Option<string>
, there are several "mapping" interfaces (this is explained in the article). We can see this done for Option
here:
declare module './HKT' {
interface URI2HKT<A> {
Option: Option<A>
}
}
The leftmost Option
is a string key, the rightmost is the actual Option
type, this is a normal record interface after all. Let's have a quick look at the definition of Type
: type Type<URI extends URIS, A> = URI2HKT<A>[URI]
.
URI2HKIT
is a map from our "Option"
to our actual Option
. It's allowing is to pass 2 generic parameters to get back our value type wrapped in our higher kinded type, solving the generics issue mentioned earlier.
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