You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Allow classes to be parametric in other parametric classes #1213
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
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
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:
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):
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
map<A,B>(f: (x: A)=>B): T<A>=>T<B>;
whereas right now map is a function that takes a mapper from type any (where your parameter name is A) to B.
Try using the --noImplicitAny flag for better results.
👍 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
Reacted by zernie, Derrick Beining, Ancil Thomas, Alexey Iskhakov, Ashkan Kh. Nazary, Otabek, David Piper, hamadaExe, Alisa Sireneva, Jason Butler and 27 more
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.
Reacted by mseddon, Steven, David Piper, Ran Lottem, Piotr Monwid-Olechnowicz, Mateja Petrovic, AwesomeObserver, Alisa Sireneva, Ailrk, Alan Song and 35 more
Reacted by Artha, Kaden Thomas, Alan Song, Wayne Van Son, Edgar Rodriguez, Aleksander Sorokin, Jonathan Vandamme, Hayden, Jesper van den Ende, Caleb Owens and 2 more
Reacted by Jesse Jackson, Emmanuel Meric de Bellefon and Gulg
Reacted by Landon Poch, Tingan Ho, Leon Adler, gtkatakura-bysoft, Asad Saeeduddin, Meirion Hughes, Eric Elliott, Chris Krycho, Nurbol Alpysbayev, Flavio Vilante and 20 more
Reacted by probit Labs, Ivan Demchenko, Jack Leigh, Flavio Vilante, AwesomeObserver, Aleksander Sorokin and nyngwang
Reacted by Flavio Vilante, Alexey Berezin, Aleksander Sorokin and matheus-thejibe
I think T<~,~> is clearer than T~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.
Reacted by Aleksander Sorokin, Соснин Алексей, 김회준 and TOPKAT
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:
the handling of type constraints
partial application
composition and other combinators
higher order types like map/lift/reduce for tuples and objects
abstraction over type constructors
pattern matching on types and other experiments
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.
Reacted by pascalvdbosch, Kirill Agalakov, Toni Villena, Justin Dalrymple, David Piper, Reinis Vesers, Charlie Jonas, Benjamin Wightman, Jan Killian, Eliav2 and 2 more
Reacted by Adrian, Diana van de Laarschot, Kirill Agalakov, Cristiano Aguzzi, chocolateboy, Matthew Halloran, Toni Villena, David P, Luis Gregson, David Piper and 6 more
Reacted by Iorgvi, Patricio Ezequiel Hondagneu Roig, Claudia Meadows, David P, Rafael Hengles, Kai Schlamp, Alex, katini, Michał Isalski, Roman Fadeev and 116 more
Reacted by Pete Smith, Package, Kai Schlamp, David Piper, Simon Nicholas Struthers, 김회준, Alejandro Hernandez, silvialeung, Andrey Bublik, LEE HYEONU and 45 more
Accept a HK type: foo<A<B>>(): ..., type Foo<A<B>> = ...
Define a HK type: 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).
Reacted by Achilleas Naoumidis, Kasper and Anna Bocharova
Craig P Hicks (@craigphicks) - ... 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).
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:
type SomeGenericType<T> = Body 1
type MetaTypeResolved<Generic Parameter and Constraints> = Body 2 which references SomeGenericType
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 SomeGenericType X Generic 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 SomeGenericType
type MetaTypeResolved = MetaType<SomeGenericType>
which would be equivalent to
type MetaTypeResolved<Generic Parameter and Constraints> = Body with VariableGenericName replaced by SomeGenericType
and that would only be a current TypeScript generic, so it would not impose any extra requirements on the inference logic.
Reacted by David Sundqvist, __ and Daniel Ferenc Balogh
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.
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 😢
Reacted by Petr Smital, Diogo Biazus, Cristiano Aguzzi, Emrah Seidov, Matthias Seemann, Peter Matta, Mohammad Hadi Aliakbar, Luca Pavan, Marcus Blättermann, JJ and 19 more
Reacted by Pete Smith, Emrah Seidov, Lou Cyx, Matthew Dean, Landon Poch, Yoav, Travis Wagner, Haileyesus Shitalem, dev-a-loper, Impurity and 1 more
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.
Reacted by btoo, Ondratra, Alex and Juan Miguel Gomez
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.
exportinterfaceOf<FextendsKind>{/** * of :: `a -> F a` */of: <A>(a: A)=>$<F,[A, ...Tail<ToDefaultParam<F['signature']>>]>;}exportinterfaceFunctor<FextendsKind>{/** * map :: `(a -> b) -> F a -> F b` */map: TypeSkell<'(a -> b) -> F a ..e -> F b ..e',{F: F}>;}exportinterfaceApplicative<FextendsKind>extendsFunctor<F>,Of<F>{/** * ap :: `F a -> F (a -> b) -> F b` */ap: TypeSkell<'F a ..x -> F (a -> b) ..y -> F b ..xy',{F: F}>;}exportinterfaceMonad<MextendsKind>extendsApplicative<M>{/** * flatMap :: `(a -> M b) -> M a -> M b` */flatMap: TypeSkell<'(a -> M b ..x) -> M a ..y -> M b ..xy',{M: M}>;;}
Reacted by Ilya Borisov, Jan Killian, Craig P Hicks, Ondratra, Diogo Biazus, Sean Newell, middle-ages, Osmar Cavalcante, Daniel Meyer, Alexandre Stahmer and 3 more
Reacted by Jan Killian, Niki, Craig P Hicks, Fabián Heredia Montiel, Mitja O, Alexandre Stahmer and jobafr
Reacted by Jan Killian, Achilleas Naoumidis, Cristiano Aguzzi, Alexandre Stahmer and Nino
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.
Reacted by Ely De La Cruz
Reacted by Giulio Caprino, Claudiu Ceia , snarbles2, Nicky McCurdy, David P, Robin Cafolla, Sean Lee, Marcus Blättermann, Aleksei Gmitron, Sergey Miryanov and 4 more
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)
Reacted by Marcel Laverdet, snarbles2, Grzegorz Szeliga, TOPKAT, Duma and Sunner
Reacted by Pete Smith, Alex Malkevich, Grzegorz Szeliga, Aleksei Gmitron, Duma, emilbowry and Daniel Wilkins
Reacted by Pete Smith, Ali Sheehan-Dare, Grzegorz Szeliga, Srijan Poudel and Duma
I don't think it is a software problem. It's a problem of research and consensus.
/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 😁
Reacted by Muryllo Pimenta
Reacted by TOPKAT, Sean Lee, cubxx and Marcus Blättermann
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
Reacted by TOPKAT, cubxx, Marcus Blättermann, emilbowry, ForkΨKillet, aweebit and Muryllo Pimenta
Reacted by David P, Aleksei Gmitron, Iceman8911, Roosemberth Palacios, NotABot-FES and Claudia Meadows
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