Skip to content
/ hkts Public

A simple encoding of higher-kinded types in TypeScript

License

Notifications You must be signed in to change notification settings

pelotom/hkts

Repository files navigation

HKTS - Higher-Kinded TypeScript Build Status

TypeScript doesn't really support higher-kinded types yet, but various attempts have been made to simulate them (see "Prior Work" at the bottom). This project is one such idea, which attempts to solve the problem via conditional types.

The idea is that a type which logically depends on a type constructor (rather than a simple type) just takes a regular type variable, and then uses the $ operator to "apply" that variable to other types:

interface Functor<F> {
  map: <A, B>(fa: $<F, [A]>, f: (a: A) => B) => $<F, [B]>;
}}

Then, to make an instance of this "type class", we supply it with a version of our higher-kinded type which has been saturated with placeholders (_) representing type "variables":

type Maybe<A> = { tag: 'none' } | { tag: 'some'; value: A };
const MaybeF: Functor<Maybe<_>> = {
  map: (maybe, f) => maybe.tag === 'none' ? maybe : { tag: 'some'; value: f(maybe.value) },
};

A type application $<T, S> recursively walks the tree of type T, substituting any placeholders _<N> it finds with the corresponding argument type S[N]. _ is shorthand for _<0>, and there are also placeholder aliases _0 = _<0>, _1 = _<1>, etc.

That's pretty much all there is to it! Take a look at the tests for more examples.

This is just a proof of concept at the moment; use at your own risk!

Prior Work

Other notable attempts to solve this problem: