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