1

So let's say I have something like this:

type Id<T> = T;
const f = <B>(a: Id<B>, b: B): [Id<B>, B] => [a, b];

But I want to extend f to be able to use other dependent types

type List<T> = T[];

How can I achieve something like

const f = <A, B>(a: A<B>, b: B): [A<B>, B] => [a, b];
f<Id, number>(1, 2) // evaluates to [1,2]
f<List, number>([1], 2) // evaluates to [[1],2]

without typescript complaining that A is not generic? Or do the types passed into a generic have to be flat types?

0-0
  • 11
  • 1

1 Answers1

0

Unfortunately, TypeScript has no direct support for higher kinded types (HKTs) of the sort you intend A to be. And you cannot refer to Id without immediately specifying its type parameter like Id<T> for some T. See microsoft/TypeScript#1213 for a longstanding, open feature request for higher kinded types. That issue does list out some ways that HKTs can be simulated/emulated in TypeScript, but nothing nice enough that I'd really recommend it.

For example, you can do something like this where you explicitly register every higher-kinded type you care about by merging it into an interface declaration:

type Id<T> = T;
interface HKTRegistry<T> { Id: Id<T> };

type List<T> = T[];
interface HKTRegistry<T> { List: List<T> }

And then you could use it like this:

const f = <A extends HKT, B>(a: Specify<A, B>, b: B): [Specify<A, B>, B] => [a, b];

const id = f<"Id", number>(1, 2);
const hmm = f<"List", number>([1], 2);

where HKT and Specify are defined like this:

type HKT = keyof HKTRegistry<any>
type Specify<H extends HKT, T> = HKTRegistry<T>[H];

Playground link to code

But it's clunky and doesn't capture things like constrained type parameters and it's hard to infer an HKT this way. As I said, it's not really in a place I'd recommend for any production code.

jcalz
  • 264,269
  • 27
  • 359
  • 360