Skip to content

How to print anonymous recursive types #463

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
JsonFreeman opened this issue Aug 15, 2014 · 12 comments
Closed

How to print anonymous recursive types #463

JsonFreeman opened this issue Aug 15, 2014 · 12 comments
Labels
Bug A bug in TypeScript Help Wanted You can do this
Milestone

Comments

@JsonFreeman
Copy link
Contributor

var a: { foo: typeof a; }

a has a recursive type. If we try to print this type, it prints { foo: any; }, but it does not behave as such in type comparisons. So we are lying when we print it.

When fixing this, it's important to consider both displaying a type to the user, and generating .d.ts files.

@danquirk
Copy link
Member

Related to #402?

@JsonFreeman
Copy link
Contributor Author

Yes, it is related

@ahejlsberg
Copy link
Member

I don't think that should be a bug. Consider:

declare function f(): { foo: typeof f; }
var g: () => { foo: typeof g; }

We currently permit both of those and correctly generate recursive types. The old compiler is oddly inconsistent in these cases, making an any in f but not in g. The new compiler handles both, as well as your initial example.

We should fix the type printer to handle the recursion.

@JsonFreeman
Copy link
Contributor Author

I think recursive types are nice, but typeToString does not print them correctly. Right now if you try to print the type of a in my initial example:

var a: { foo: typeof a; };

You get { foo: any; }, which is not the correct type. That means if you have the following test:

var a: { foo: typeof a; };
var a: { foo: any; };

You will get an error that the types are not identical, yet the resulting error will tell you that both types are { foo: any; }. We could say that recursive types are allowed and this is just a bug in typeToString. But then we'd also have to make sure that it works in .d.ts generation.

@JsonFreeman
Copy link
Contributor Author

Also, if we keep the type recursive and do not change it to { foo: any; }, then we have to do that for all the following cases too:

var a = { foo: a };
var b = [b];
var c = () => c;

@JsonFreeman
Copy link
Contributor Author

And then we'd get into inconsistencies when we have something of the following form:

var a = foo(a);

for some function foo.
The reason we do not have this problem for function declarations, module instance types, and class constructor types is that we already know something about the structure of these types, just by virtue of the semantics of declaring these entities. For example, an instantiated module will always be an object type whose properties are its exports, and therefore we have a structural basis for making a recursive type that behaves consistently.
For vars, we do not have this luxury because a var can have any type at all. We have no basis for defining the structure of a type, and then making it recursive because we have to resolve the expression on the right hand side before we know anything.
Unless you are saying that we should support recursive types only in the type annotation of a var, and not its initializer! This is plausible, but then we would have to have a special check for the non-contractive case:

var a: typeof a;

@ahejlsberg
Copy link
Member

Yes, I was talking recursive types in type annotations. This is something we already support and we shouldn't undo that simply because we can't produce an accurate string representation of those types. I think the current solution of outputting any when recursion is detected in an unnamed type is a reasonable compromise.

We might consider outputting ... instead, perhaps after a couple of iterations:

{ foo: { foo: { foo: ... } } }

But that obviously is more work and might cause us to generate an invalid .d.ts unless we also include logic to produce errors in such cases.

@JsonFreeman
Copy link
Contributor Author

I don't think we should deemphasize the importance of printing the type correctly. It is a big part of the user experience of the language. I do think that we should ensure we have a reasonable way to print a type before trying to support it in the type system. One thing the user should never get is a message like the following:

Subsequent variable declarations must have the same type. Variable 'a' must be of type '{ foo: any; }', but here has type '{ foo: any; }'.

This is the result of trying to compile the example I provided above:

var a: { foo: typeof a; };
var a: { foo: any; };

@JsonFreeman
Copy link
Contributor Author

I'm going to reopen this bug and change the title. This bug is still tracking the issue for actually printing the types correctly. There is good material here we can use for that. Also going to mention design bug #517, since it's very relevant here.

@JsonFreeman JsonFreeman reopened this Sep 8, 2014
@JsonFreeman JsonFreeman changed the title Anonymous recursive type in the compiler implementation How to print anonymous recursive types Sep 8, 2014
@mhegazy mhegazy modified the milestones: TypeScript 1.3, TypeScript 1.4 Oct 2, 2014
@mhegazy mhegazy modified the milestones: Community, TypeScript 1.4 Nov 20, 2014
@mhegazy mhegazy added the Help Wanted You can do this label Feb 20, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Feb 20, 2016

A fix here should handle the case outlined in #475.

@Andarist
Copy link
Contributor

To the best of my understanding of the issue at hand - this is already fixed and the issue could be closed cc @RyanCavanaugh

@RyanCavanaugh
Copy link
Member

Wow, throwback. Indeed. Thanks @Andarist !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Help Wanted You can do this
Projects
None yet
Development

No branches or pull requests

9 participants