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

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.