Skip to content

Compile Error when using generic type with [K in keyof T]?: XXX #31070

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
shaunxu opened this issue Apr 23, 2019 · 7 comments · Fixed by #32071
Closed

Compile Error when using generic type with [K in keyof T]?: XXX #31070

shaunxu opened this issue Apr 23, 2019 · 7 comments · Fixed by #32071
Assignees
Labels
Bug A bug in TypeScript Domain: Mapped Types The issue relates to mapped types

Comments

@shaunxu
Copy link

shaunxu commented Apr 23, 2019

TypeScript Version: 3.4.4

Search Terms:
keyof index-properties generic-type

Code

type Numeric<T> = {
    [K in keyof T]?: number
}

class Entity {
    aNumber: number;
}

const fn = <T extends Entity>() => {
    const n: Numeric<T> = {
// .      ^
// .      Type '{ aNumber: number; }' is not assignable to type 'Numeric<T>'
        aNumber: 1
    };
};

Expected behavior:
Should not report compile error since in the type constraint of function fn was <T extends Entity> which means T should have aNumber: number and { aNumber: 1 } should be satisfied.

Actual behavior:
Compile error said Type '{ aNumber: number; }' is not assignable to type 'Numeric<T>'.

Playground Link:
https://www.typescriptlang.org/play/index.html#src=type%20Numeric%3CT%3E%20%3D%20%7B%0D%0A%20%20%20%20%5BK%20in%20keyof%20T%5D%3F%3A%20number%0D%0A%7D%0D%0A%0D%0Aclass%20Entity%20%7B%0D%0A%20%20%20%20aNumber%3A%20number%3B%0D%0A%7D%0D%0A%0D%0Aconst%20fn%20%3D%20%3CT%20extends%20Entity%3E()%20%3D%3E%20%7B%0D%0A%20%20%20%20const%20n%3A%20Numeric%3CT%3E%20%3D%20%7B%0D%0A%2F%2F%20.%20%20%20%20%20%20%5E%0D%0A%2F%2F%20.%20%20%20%20%20%20Type%20'%7B%20aNumber%3A%20number%3B%20%7D'%20is%20not%20assignable%20to%20type%20'Numeric%3CT%3E'%0D%0A%20%20%20%20%20%20%20%20aNumber%3A%201%0D%0A%20%20%20%20%7D%3B%0D%0A%7D%3B

Related Issues:
https://stackoverflow.com/questions/55804034/keyof-reported-compile-error-when-using-generic-type

@barld
Copy link

barld commented Apr 23, 2019

T could also be a child of Entity with extra fields. These fields are now not included.

@shaunxu
Copy link
Author

shaunxu commented Apr 23, 2019

T could also be a child of Entity with extra fields. These fields are now not included.

But the properties defined in Numeric<T> are all optional. So I think even though T could be any child of Entity with more properties, they are optional in Numeric<T> which is OK.

@jack-williams
Copy link
Collaborator

I can see why this possibly should work: there is special handling for assigning empty object types to optional generic mapped types. This works:

const fn = <T extends Entity>() => {
    const n: Numeric<T> = {};
    n.aNumber = 1;
};

Though IMO there is always a red flag when assigning something concrete to something generic, in particular regarding future compatibility with homomorphic mapped types and new modifiers.

@jcalz
Copy link
Contributor

jcalz commented Apr 23, 2019

EDIT: Never mind! 😅

Looks like a duplicate of #13442 with the error catching such (rare and possibly not important) issues as this:

class SevenEntity extends Entity {
  aNumber: 7;  // narrowed property type, acceptable
}

fn<SevenEntity>(); 

Inside fn, the n constant is annotated as type { aNumber?: 7 } but is not one of those. 💥

@hackape
Copy link

hackape commented Apr 23, 2019

@jcalz make sense if the case is:

const fn = <T extends Entity>() => {
  // let's pretend T is SevenEntity
  // then error report is justified, understood
  const n: T = { aNumber: 1 };
};

However, we're talking about const n: Numeric<T>, and in this case, Numeric only cares about the key, not value. Yes, I can understand TS guards user super carefully value-wise, but key-wise this behavior looks buggy to me.

@jcalz
Copy link
Contributor

jcalz commented Apr 23, 2019

Yeah, you are right. It's not a duplicate of #13442 because only the keys and not the values are involved in the mapped type. I retract my statement!

@shaunxu
Copy link
Author

shaunxu commented Apr 25, 2019

@jack-williams Thanks for your workaround. But I may not be able to force use to specify n to an empty object and assign properties.

Just an update. If I defined type Numeric<T> = (keyof T)[];. Then in the following function I can use const n: Numeric<T> = [ "aNumber" ]; without any error. I my understanding (keyof T)[] should have the same restriction as { [K in keyof T]?: number; } but one is OK the other is not.

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: Mapped Types The issue relates to mapped types
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants