Skip to content

Recursive Mapped Conditional Type inferring top-level children to any #25860

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
osdiab opened this issue Jul 22, 2018 · 3 comments
Closed

Recursive Mapped Conditional Type inferring top-level children to any #25860

osdiab opened this issue Jul 22, 2018 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@osdiab
Copy link

osdiab commented Jul 22, 2018

TypeScript Version: 3.1.0-dev.20180721

Search Terms: any recursive mapped conditional inference

Code

    interface MyNumbers {
      cool: {
        mysteriouslySo: number;
        comfortablySo: number;
      };
      scary: {
        ominouslySo: number;
      };
    }
    
    type MapLeaves<OldShape, NewLeaf> = OldShape extends {}
      ? { [k in keyof OldShape]: MapLeaves<OldShape[k], NewLeaf> }
      : NewLeaf;
    
    // This is inferring as { cool: any, scary: any }, not what I expected!
    type MyStrings = MapLeaves<MyNumbers, string>;

Expected behavior:

I'm not sure if I'm misusing recursive mapped conditional types here, but I expected MyStrings to be

    interface MyNumbers {
      cool: {
        mysteriouslySo: string;
        comfortablySo: string;
      };
      scary: {
        ominouslySo: string;
      };
    }

Actual behavior:
It's inferred as

{ cool: any, scary: any }

Playground Link:
Link

@ahejlsberg
Copy link
Member

Duplicate of #23897. The types are actually computed correctly, but our type display logic doesn't currently descend into recursive mapped types because they could potentially be infinite.

type MapLeaves<OldShape, NewLeaf> = OldShape extends object
  ? { [k in keyof OldShape]: MapLeaves<OldShape[k], NewLeaf> }
  : NewLeaf;

type ShouldBeString = MapLeaves<MyNumbers, string>["cool"]["comfortablySo"];

Here, ShouldBeString is indeed of type string. By the way, note that I had to change {} to object in your conditional type. The {} type is actually a supertype of every type but null or undefined, whereas object is a supertype of only objects (and not primitive types).

@ahejlsberg ahejlsberg added the Duplicate An existing issue was already created label Jul 22, 2018
@osdiab
Copy link
Author

osdiab commented Jul 22, 2018

Ah, interesting. Sounds like displaying it with some kind of ellipsis or placeholder type would be more ideal. I'll post to that issue.

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants