Skip to content

Type alias resolves to different type than placing the type inline #15973

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
LennyLixalot opened this issue May 20, 2017 · 4 comments
Closed
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@LennyLixalot
Copy link

TypeScript Version: 2.3.1, VS 2017 Preview 2

Code

// A *self-contained* demonstration of the problem follows...
type S = { items: {whatever:true}};
type ExtractedItems<T> = ({[K in keyof (T | { items: {} })]: { [key: string]: T[K] }} & { items: {} })["items"];
let extractedInline:     ({[K in keyof (S | { items: {} })]: { [key: string]: S[K] }} & { items: {} })["items"];
let extractedWithType: ExtractedItems<S>;

Expected behavior:
extractedInline and extractedWithType should have the same type.
Actual behavior:
extractedInline is typed as expected, extractedWithType is typed as {}.

image

image

If this behaviour IS in some way expected, that's not made clear in the documentation. Our natural assumption is that a type alias used in this manner to provide a level of indirection should not change the type.

@mhegazy
Copy link
Contributor

mhegazy commented May 22, 2017

When used as a generic, keyof (T | { item: {} }) becomes never, since T is not known to have any keys. that is not the case when the type is specified explicitlly. if you are sure T will have an item, then add a constraint, even something like:

type ExtractedItems<T extends {items?: any}> = ({[K in keyof (T | { items: {} })]: { [key: string]: T[K] }} & { items: {} })["items"];

@mhegazy mhegazy added the Working as Intended The behavior described is the intended behavior; this is not a bug label May 22, 2017
@LennyLixalot
Copy link
Author

@mhegazy Ahh, well that won't work for us then. We were using that as a (hacky) filter system to say "do this if T has items".

Why does keyof (T | { item: {} }) not use the generic type of T? Is this generics behaviour documented somewhere?

Thanks for taking the time to reply, sorry that we're about to be really difficult about it.

Reading through the spec, it appeared to say wrapping in a type alias should make no difference. In 3.7 Named Types is says

TypeScript has a structural type system, and therefore an instantiation of a generic type is indistinguishable from an equivalent manually written expansion. For example, given the declaration

and similarly 3.10 Type Aliases says

Writing a reference to a non-generic type alias has exactly the same effect as writing the aliased type itself, and writing a reference to a generic type alias has exactly the same effect as writing the resulting instantiation of the aliased type.

There's no obvious reference in the spec to the behaviour you describe, and nothing in the spec stands out as describing how constraints are used.

3.6.1 Parameter List is the closest we could see to describing this behaviour, but it's not immediately obvious that it accounts for this.

in property accesses (section 4.13), new operations (section 4.14), and function calls (section 4.15), type parameters appear to have the members of their base constraint, but no other members.

You say

When used as a generic, keyof (T | { item: {} }) becomes never

This doesn't appear to be true in the general case though, in fact the whole thing works as we expected it to until the ["items"] on the end. We recognise that this may be the above quoted behaviour described in 3.6.1 of the spec, but it's certainly not intuitive that the rest of that type alias works until the ["items"].

Apologies if there's something obvious we're not getting and wasting your time with here. We recognise that this likely is working as intended, but feel the documentation could use some improvement here as it's certainly not working as we would have intuitively expected.

@mhegazy
Copy link
Contributor

mhegazy commented May 22, 2017

@mhegazy Ahh, well that won't work for us then. We were using that as a (hacky) filter system to say "do this if T has items"

that is something that is not supported currently, you probably want #12424

@mhegazy
Copy link
Contributor

mhegazy commented Jun 6, 2017

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

@mhegazy mhegazy closed this as completed Jun 6, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

2 participants