Skip to content
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

Repeated mapped type inference causes incorrect typeToString results #23897

Open
RyanCavanaugh opened this issue May 4, 2018 · 10 comments · Fixed by #28494
Open

Repeated mapped type inference causes incorrect typeToString results #23897

RyanCavanaugh opened this issue May 4, 2018 · 10 comments · Fixed by #28494
Labels
Bug A bug in TypeScript Domain: Type Display Bugs relating to showing types in Quick Info/Tooltips, Signature Help, or Completion Info
Milestone

Comments

@RyanCavanaugh
Copy link
Member

TypeScript Version: 2.8.0-insiders.20180320

Search Terms: mapped type inference display typetostring

Code

Via https://stackoverflow.com/questions/50181650/type-inference-lost-inner-level-types-in-typescript

interface IAction {
    type: string;
}

type Reducer<S> = (state: S, action: IAction) => S

function combineReducers<S>(reducers: { [K in keyof S]: Reducer<S[K]> }): Reducer<S> {
    const dummy = {} as S;
    return () => dummy;
}

const test_inner = (test: string, action: IAction) => {
    return 'dummy';
}
const test = combineReducers({
    test_inner
});

const test_outer = combineReducers({
    test
});

// '{test: { test_inner: any } }'
type FinalType = ReturnType<typeof test_outer>;

var k: FinalType;
k.test.test_inner // 'string'

Expected behavior: FinalType's hover type should be { test: { test_inner: string } }

Actual behavior: Shows as { test: { test_inner: any } }

Playground Link: Link

@DanielRosenwasser DanielRosenwasser added Bug A bug in TypeScript Domain: Type Display Bugs relating to showing types in Quick Info/Tooltips, Signature Help, or Completion Info labels May 4, 2018
@RyanCavanaugh
Copy link
Member Author

Better repro from #25295

class Foo<A> {
  a: A;
}

class ObjectFoo<A> extends Foo<A> {
  constructor(private b: { [k in keyof A]: Foo<A[k]> }) {
    super();
  }
}

const a = new ObjectFoo({ b: new Foo<string>() });
const obj = new ObjectFoo({ a });

@mhegazy mhegazy added this to the TypeScript 3.0 milestone Jun 28, 2018
@weswigham
Copy link
Member

@sandersn didn't we explcitly limit reverse mapped type output to one level deep for lack of a better method of stopping infinite recursion? Is this potentially related?

@Jomik
Copy link

Jomik commented Jun 29, 2018

This is a pretty big deal breaker for my validator library 😢 https://github.com/Jomik/cerberus
Is it not possible to either display more layers, 9 or so. Or detect recursion and display that rather than "any"?

@osdiab
Copy link

osdiab commented Jul 22, 2018

In my case of a recursive type exhibiting this error (#25860), sounds like displaying it with some kind of ellipsis or placeholder type would be nicer than just dropping down to any—that's a misleading type, given that it actually removes information from the display of the type. Not sure what concerns there would be though since I don't know how these serialized types ultimately get used, but in the case of VSCode type display the current behavior isn't great.

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Aug 21, 2018

Ellipsis isn't great, in my opinion, unless some kind of "truncate" flag was enabled or something.
Having the type hidden in ellipsis slows down a developer when they have to figure out what's in the ellipsis. (TS 3.0 seems to do that for types that are too long at the moment and it slows me down quite a bit when having to figure out what's truncated)

Displaying the detected recursive type (without expansion) would be much better than any or ..., for generic types that have not had concrete types substituted in the parameters.

I think TS already does this,

type Recursive<T> = { r: T | Recursive<T> };
//Recursive<number>
declare const r: Recursive<number>;

type A = number;
//number
declare const a: A;

However, if it can somehow deduce that there's no infinite recursion, or that it stops at a reasonable depth, when a concrete type has been substituted in, it should just display the full type.

declare function schema<T> (value : T) : (
    {
        field : T
    }
);

//Should be { field : { field : boolean } }
//But is { field : any }
const x = schema(schema(true));

//No matter how many times I call schema<>(),
//It should show the full type
//because it isn't an infinitely recursive type.
//In this case, it's just 4 levels deep
const y = schema(schema(schema(schema(true))));

Edit: Looks like my issue is related to this one, #26228

As for ellipsis not being great (in my opinion), #26238

@KaelWD
Copy link

KaelWD commented Sep 1, 2018

I seem to be getting the opposite of this. VSCode shows what I expect the type to be, but tsc thinks it's never
image
image
image

I'll try to get a simple repro together, VExpansionPanelContentInstance is a Vue component with some circular references.

@codeaid
Copy link

codeaid commented Sep 28, 2018

Same here. Here's some code to give a rough idea of what I have going on (not tested):

import { User } from 'oidc-client';
import { combineReducers } from 'redux';
import { Action, handleActions } from 'redux-actions';

const enum AuthActionType {
  Callback = 'AUTH_CALLBACK',
}

const userReducer = handleActions(
  {
    [AuthActionType.Callback]: (state: State, action: Action<User>) => >action.payload,
  },
  null
);

const authReducer = combineReducers({
  user: userReducer,
});

const rootReducer = combineReducers({
  auth: authReducer,
});

type State = ReturnType<typeof rootReducer>;

At this point State is showing as

type State = {
  auth: {
    user: any
  }
}

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Sep 28, 2018

I'm not sure if I've mentioned this before but I'll just add this again.

This isn't just a type display issue. It's also a declaration emit issue.

The display will show any.
When compiling with tsc, it will check the "real" type and not use any.
But the emitted declaration files will have any.

[EDIT]
At least, from what I've experienced.

@Jomik
Copy link

Jomik commented Nov 13, 2018

@weswigham Sure you don't want to leave this open? As it seems like the issue still exists after the PR.

@weswigham
Copy link
Member

Oooo GitHub not picking up on the word "doesn't" before "fix". 🌞

@weswigham weswigham reopened this Nov 13, 2018
@DanielRosenwasser DanielRosenwasser removed this from the TypeScript 3.2 milestone Nov 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Domain: Type Display Bugs relating to showing types in Quick Info/Tooltips, Signature Help, or Completion Info
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants