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
Type aliases and interfaces are subtly different and there are rules about self-recursion for aliases that don't apply to interfaces. Because an alias is supposed to always be "immediately expandable" (it's as if it were an in-place expansion of its referand), there are things you can do interfaces you can't do with type aliases.
Cycles can be detected and you can give up at that point, but as long as you can topologically order the type aliases by their references to one another it's fine. Haskell, PureScript, Scala etc have no problem with this.
Reacted by Mathieu CAROFF, D Stibbe, Cristian Lozano, rvk220, Michael Sereniti, Sam Vervaeck and silverwind
I mean, we could, it's just that writing interface Bar<T> extends Foo<T> { } accomplishes the same thing without requiring us to rearchitect how type aliases work
Reacted by c69 and Maciek Sakrejda
Reacted by Michael Sereniti, Nathan Chappell, Izaak Schroeder, Nolan Amy, Ethan Standel and silverwind
In this situation afaict the only solution is to expand the definitions of Bar and Baz into the union, substituting Foo for their type variable. Bar and Baz (and however many other alternatives) could be large complex interfaces, and must now each be maintained as 2 separate copies that must be kept in sync with one another.
Reacted by Mathieu CAROFF, D Stibbe, Cristian Lozano, Michael Sereniti, Jesse Kelly, Curtis Fenner and Marvin
I'm trying to create a type that circularly references itself, but in a way that it makes sense - or at least it does to me. This is basically the same problem as Tom Crockett (@pelotom) is having.
If I modify it to the following, it works as intended, but this really is just more work for the user. This gets even worse when the data types are more complicated than a simple key-value pair.
Reacted by Marco Manino, Mikaël Mayer, fregante, Mathieu CAROFF, Maciej Bukowski, Daniel Ly, Hugh Collins, Marek Beňovič, Shaked Ofek, Andrew Kaiser and 31 more
Reacted by Daniel Ly, Alkis Mavridis, anton-kravchenko, Artur Klesun and Memfisrain
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
Reacted by Bailey Stoner, Matt Stephens, Nathan Baum, TOPKAT, Gili Tzabari, Marvin and silverwind
Bob van der Linden (@bobvanderlinden) This is, as far as I know, the only workaround for now. Inlining them doesn't work because circular referencing is not allowed. So why is it allowed if you put an extra step between the two? It's black magic that might have something to do with lazy evaluation, but it's definitely beyond my knowledge on the internal workings of the type system.
With #33050 the original example can now be written as:
interface Bar<A> { x: A }
type Foo = Bar<Foo>
Reacted by James Conkling, Kevin Beal, kgtkr, Tom Crockett, Donald Pipowitch, SlurpTheo, Paweł Lula, Brandon Chinn, Artem Zakharchenko, Lucas Basquerotto and 8 more
Reacted by Luis Herranz, Luke Winship and Sagnik Pradhan
This is working fine for me so far. *crosses fingers*
typeJSONValue=|null|boolean|number|stringtypeJSONArray=|JSONValue[]|JSONArray[]|JSONMap[]// Use interface because type errors://// type JSONMap = Record<string, JSONValue | JSONArray | JSONMap>// -> Type alias 'JSONMap' circularly references itself. ts(2456)//interfaceJSONMap{[key: string]: |JSONValue|JSONArray|JSONMap}
- Type alias 'PlaywrightNodeSnapshot' circularly references itself.+ Type alias 'PlaywrightNodeSnapshot' circularly references itself. Did you mean to spread NodeSnapshot[]?
Activity
KiaraGrouwstra commentedon Jun 12, 2017
See the explanation here for more info.
RyanCavanaugh commentedon Jun 12, 2017
Illegal because we don't know that the definition of
Barisn'tpelotom commentedon Jun 12, 2017
Don't you have access to the definition of
Barso you could check that?RyanCavanaugh commentedon Jun 12, 2017
Barmight beand now we're going infinitely deep.
Type aliases and interfaces are subtly different and there are rules about self-recursion for aliases that don't apply to interfaces. Because an alias is supposed to always be "immediately expandable" (it's as if it were an in-place expansion of its referand), there are things you can do interfaces you can't do with type aliases.
pelotom commentedon Jun 12, 2017
Cycles can be detected and you can give up at that point, but as long as you can topologically order the type aliases by their references to one another it's fine. Haskell, PureScript, Scala etc have no problem with this.
RyanCavanaugh commentedon Jun 12, 2017
I mean, we could, it's just that writing
interface Bar<T> extends Foo<T> { }accomplishes the same thing without requiring us to rearchitect how type aliases workpelotom commentedon Jun 13, 2017
Sure, you can do that to emulate
But what about
In this situation afaict the only solution is to expand the definitions of
BarandBazinto the union, substitutingFoofor their type variable.BarandBaz(and however many other alternatives) could be large complex interfaces, and must now each be maintained as 2 separate copies that must be kept in sync with one another.skreborn commentedon Jun 27, 2017
I'm trying to create a type that circularly references itself, but in a way that it makes sense - or at least it does to me. This is basically the same problem as Tom Crockett (@pelotom) is having.
If I modify it to the following, it works as intended, but this really is just more work for the user. This gets even worse when the data types are more complicated than a simple key-value pair.
This type allows me to create values like the following and handle them in a type safe way even when they are deeply nested.
Ryan Cavanaugh (@RyanCavanaugh) Is there any other (sane) way to create type-safe nested objects?
mhegazy commentedon Aug 17, 2017
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
pelotom commentedon Aug 17, 2017
I think this is a reasonable feature request for reasons that are made clear in the comments, can we reopen please?
23 remaining items
skreborn commentedon Nov 24, 2018
Bob van der Linden (@bobvanderlinden) This is, as far as I know, the only workaround for now. Inlining them doesn't work because circular referencing is not allowed. So why is it allowed if you put an extra step between the two? It's black magic that might have something to do with lazy evaluation, but it's definitely beyond my knowledge on the internal workings of the type system.
babakness commentedon Nov 24, 2018
You can sort of induce lazy evaluation by using the following pattern
Its ugly but it works avoiding circular reference problems.
pronebird commentedon Feb 28, 2019
I found that cyclic references work very well with nested objects
But as soon as I add an
Arrayinto the mix and attempt to type it, the whole thing breaks, i.e:Full source:
flux627 commentedon Aug 7, 2019
Here's my work-around:
kevinbeal commentedon Aug 14, 2019
I wanted to do this:
But I had to do this workaround:
Maybe this will help someone else.
ahejlsberg commentedon Aug 30, 2019
With #33050 the original example can now be written as:
amatiasq commentedon Jun 8, 2021
Maybe that worked in a previous version but I'm getting this:
RebeccaStevens commentedon Jan 27, 2022
Is it possible to write a tuple type that repeats a pattern?
zaydek commentedon Mar 16, 2022
This is working fine for me so far. *crosses fingers*
gajus commentedon Mar 27, 2023
Rebecca Stevens (@RebeccaStevens) did you ever figure this out? I asked a related question:
https://stackoverflow.com/q/75851865/368691
RebeccaStevens commentedon Mar 27, 2023
No. The best I came up with was:
gajus commentedon Mar 27, 2023
ah, I missed
[]in my question/example.The above should have been:
The circular reference error threw me off.
Maybe it would make sense to change the error to: