Repository navigation
Allow classes to be parametric in other parametric classes #1213
Description
This is a proposal for allowing generics as type parameters. It's currently possible to write specific examples of monads, but in order to write the interface that all monads satisfy, I propose writing
interface Monad<T<~>> {
map<A, B>(f: (a: A) => B): T<A> => T<B>;
lift<A>(a: A): T<A>;
join<A>(tta: T<T<A>>): T<A>;
}Similarly, it's possible to write specific examples of cartesian functors, but in order to write the interface that all cartesian functors satisfy, I propose writing
interface Cartesian<T<~>> {
all<A>(a: Array<T<A>>): T<Array<A>>;
}Parametric type parameters can take any number of arguments:
interface Foo<T<~,~>> {
bar<A, B>(f: (a: A) => B): T<A, B>;
}That is, when a type parameter is followed by a tilde and a natural arity, the type parameter should be allowed to be used as a generic type with the given arity in the rest of the declaration.
Just as is the case now, when implementing such an interface, the generic type parameters should be filled in:
class ArrayMonad<A> implements Monad<Array> {
map<A, B>(f: (a:A) => B): Array<A> => Array<B> {
return (arr: Array<A>) => arr.map(f);
}
lift<A>(a: A): Array<A> { return [a]; }
join<A>(tta: Array<Array<A>>): Array<A> {
return tta.reduce((prev, cur) => prev.concat(cur));
}
}In addition to directly allowing compositions of generic types in the arguments, I propose that typedefs also support defining generics in this way (see issue 308):
typedef Maybe<Array<~>> Composite<~> ;
class Foo implements Monad<Composite<~>> { ... }The arities of the definition and the alias must match for the typedef to be valid.
Activity
DanielRosenwasser commentedon Nov 19, 2014
Not to make any rash assumptions, but I believe you're typing it incorrectly. All parameter types require parameter names, so you probably meant to type
whereas right now map is a function that takes a mapper from type
any(where your parameter name isA) toB.Try using the
--noImplicitAnyflag for better results.metaweta commentedon Nov 19, 2014
Thanks, corrected.
metaweta commentedon Nov 28, 2014
I've updated my comment into a proposal.
fdecampredon commentedon Jan 27, 2015
👍 higher kinded type would be a big bonus for functional programming construct, however before that I would prefer to have correct support for higher order function and generic :p
RyanCavanaugh commentedon Apr 28, 2015
Quasi-approved.
We like this idea a lot, but need a working implementation to try out to understand all the implications and potential edge cases. Having a sample PR that at least tackles the 80% use cases of this would be a really helpful next step.
metaweta commentedon Apr 28, 2015
What are people's opinions on the tilde syntax? An alternative to
T~2would be something likethat allows direct composition of generics instead of needing type aliases:
DanielRosenwasser commentedon Apr 28, 2015
It's odd to have explicit arity since we don't really do that anywhere else, so
is a little clearer, though, I know other languages use
*in similar contexts instead of~:Though taking that point to an extreme, you might get:
metaweta commentedon Apr 28, 2015
I think
T<~,~>is clearer thanT~2, too. I'll modify the proposal above. I don't care whether we use~or*; it just can't be a JS identifier, so we can't use, say,_. I don't see what benefit the=>notation provides; all generics take some input types and return a single output type.169 remaining items
geoffreytools commentedon Oct 24, 2022
I made some progress on this issue on the user end of things : free-types
I focused my implementation on general-purpose type-level programming, so I included things like:
I didn't do an amazing job at providing an overview of the functionalities in the readme, because there are just so many things you can do with something like that, but there is a good guide which explains how the library works, how it's designed and the limitations it has.
I used it in the project type-lenses to enable reaching the values of arbitrary types or manipulating them with arbitrary type-level functions, and in ts-spec to implement equality. I'm also using it in a project that aims at converting a class to a collection of curried functions with all the types correctly wired, which could be a way to easily turn a fantasy-land compliant class into a static-land-like collection of functions, but also any arbitrary class, with some more work from the part of the implementer.
I need advice regarding performance: stacking combinators and compositions on top of each other adds up, but I don't have a solid way of evaluating my design decisions to make the building blocks as lightweight as possible. The value of reusing types to an extreme is questionable but I find it to be an interesting question ;)
There are also limitations and pain points which may have workarounds or even solutions I don't know about.
I don't know if this approach is a dead end but I definitely hit a wall at some point and I don't imagine a full featured and performant solution can be implemented solely on the user end.
J-Cake commentedon Nov 20, 2022
Alternative syntax proposal: Using the
inferkeyword outside of a conditional clause:This toy example obviously simplifies to
but that doesn't matter.
exoRift commentedon Jan 4, 2023
"2014" 😔
craigphicks commentedon Dec 12, 2023
About the syntax - I think arity can be inferred from usage. More important would be
For example
type*rather thanmetatypeor some other alphabetic identifier becausemetatypeis not reserved.Most of the type checking could be deferred to the instance call
I think it is more clear for the reader and probably more clear to implement.
dead-claudia commentedon Dec 15, 2023
Craig P Hicks (@craigphicks) You could go one simpler: ditch the
type*and just use what's there.foo<A<B>>(): ...,type Foo<A<B>> = ...type Foo<A><B> = ...,interface Foo<A><B> { ... }The hard part of implementing this isn't the syntax though - it's the semantics. Retrofitting higher-kinded types into a language with type inference almost always requires at least a partial rewrite of the type checker as it mucks with inference a lot (and can rapidly slow it down).
craigphicks commentedon Dec 16, 2023
Claudia Meadows (@dead-claudia) Achilleas Naoumidis (@achinaou)
Note: I originally posted it as a separate proposal but was redirected here as my proposal was claimed to be a duplicate.
What I am proposing imposes no extra logic on the type checker - because the meta type is never evaluated until all it's parameters have been supplied, at which time it rendered to a type.
The original proposal 1213 is a lot more powerful - but also much harder to implement.
I am starting from a very simple simple problem:
For each
SomeGenericType, Body 2 has to be rewritten.This proposal is just to get around that limitation, so that the body can be written once, and applied to many instances of
SomeGenericTypeXGeneric Parameters.That is the simplest version, the result of which has no unresolved generic parameters, so obviously no inference is involved.
The next level of complexity would be to implement resolving just
SomeGenericTypewhich would be equivalent to
and that would only be a current TypeScript generic, so it would not impose any extra requirements on the inference logic.
matthew-dean commentedon Jan 17, 2024
BeppoBert (@PhilippDehler) I don't see how your solution addresses the problem of passing generics to generics, since the return types are fixed and not generic.
IARI commentedon Jun 6, 2024
I dream of functional programming with TS types - I really wish that there was a chance that at some point this could happen.
But, checking in on this every other year, it doesn't look like it 😢
geoffreytools commentedon Jun 6, 2024
Julian Jarecki (@IARI) did you have a look at Effect-TS? It uses a lot of functional patterns with a very pragmatic approach. The job they are doing is incredible. Its implementation of HKT is sufficient for its needs, with a fixed number of parameters of a specific variance. On my end I am working on improving free-types API and behaviour but I am convinced that you need some framework to do FP in TS comfortably, or you need to be prepared for basically rewriting it yourself.
ecyrbe commentedon Jun 22, 2024
if anyone want inspiration for HKT with any number of parameters,
You can take a look at my experiment with typeskell library.
A lot of mata type programming going on here, with a template string literal type (haskell like syntax) to typescript type (without using a compiler, it's all typescript native.
https://github.com/ecyrbe/typeskell
example for defining a monad :
rkirov commentedon Mar 15, 2026
Asked Claude Code to implement this based on the syntax that I prefer - kinda Haskell, but with explicit argument arity -
*,* -> *,(*, *) -> *,* -> * -> *etc. It managed to vibe code something in a few hours, so I am sharing here - https://github.com/rkirov/TypeScript . It also made a small playground with Monaco - https://rkirov.github.io/TypeScript/ so you can see the syntax and errors.It is probably extremely buggy (I haven't even looked at the code), but could be of interest to someone exploring this further. No comment on whether TS should be adding this or not. Implementation was always probably one of the easier parts anyways, and now with AI it is an even lower-hanging fruit.
Artazor commentedon Mar 23, 2026
Will try again after almost 10 years of silence. It is extremely beautiful thing. Now I'm equipped with AI. Will try to roll-out something at 20 Jul 2026 (Just funny, exactly 10 years)
laverdet commentedon Mar 23, 2026
I don't think it is a software problem. It's a problem of research and consensus.
gund commentedon Mar 23, 2026
/sarcasm we can also delegate research and consensus to AI and just accept whatever it spits out - no one is coding by hand anymore, so why not let AI design it however it sees it fit at this point 😁
exoRift commentedon Mar 23, 2026
At that point, we don't need this feature because AI can just cast as any and we should trust it to be right because AI should be making the decisions for us :D