-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Infer object type from string literal to keyof T #19227
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
Infer object type from string literal to keyof T #19227
Conversation
1. When inferring from a string literal [union] type to a index type, infer a object type with properties whose names match the constituents of the union type. The type of all the properties is `{}`. 2. Infer index types contravariantly. For example, when inferring `"foo" | "bar"` to `keyof T`, the inference algorithm now infers `{ foo: {}, bar: {} }` to `T`. When inferring `string` to `keyof T`, the inference algorithm now infers `{ [s: string]: {} }` to `T`.
Let's try to chat about this some time this week. There are definitely some scenarios I'd like to see if we can make better with this. |
Good idea — without some additional motivation, @ahejlsberg and I were not sure whether fixing this bug was worth the extra code. I just wrote it because I got started on the problem and wanted to see it through. |
Here's a simplified version of the vue d.ts that would benefit from this inference: declare function before<T>(propertyNames: T[]): Record<T, any>;
let v1 = before(['foo', 'bar'])
v1.foo = v1.bar // ok, two properties exist, both are type any
v1.foo.who = 'knows'
declare function after<T>(propertyNames: (keyof T)[]): T;
let v2 = after(['foo', 'bar'])
let v3 = after<{ foo: number, bar: string }>(['foo', 'bar'])
v2.foo = v2.bar // ok, two properties exist, both are type {}
v3.foo = v3.bar // error, two properties exist, but their types differ
v2.foo.who = 'knows' // error, foo: {} and has no property `who` Notice that the version with inference to keyof allows users to specify the type parameter explicitly and give types to their properties. This is not possible with the current version of vue.d.ts. However, in the inferred case, vue.d.ts actually needs the resulting properties to be of type |
@sandersn is this ready to go? |
No, @DanielRosenwasser @ahejlsberg and I need to decide whether to infer |
Sorry for the late reply. Quite busy with my day jobs these days. Inferring from Currently |
Nice catch @HerringtonDarkholme! Looks like I forgot to change it to any before merging. I’ll send out another PR. |
OK, the fix is at #21271. |
Will it trigger a |
@gcnew Yes, I also think unconditional However, if we do trigger a declare function inference1<T>(name: keyof T): T;
inference('foo') // implicit any For trivial code like above users can provide type annotation in the function call. But real world usage might have Another expedient is generating different type under different flags, it is not ideal. But it might be the most convenient inference for users. And we do have flag sensitive inference like narrowing implicit any. What's your opinion? |
Yes, I agree that inferring I'm not a huge fan of altering the types based on flags. I've seen code that compiles fine with the strictest flags but not without them. Unfortunately, I don't understand the use-cases very well and I'm unable to see what other options there are or the current limitations. |
Fixes #18715
Supercedes #19159
{}
.For example, when inferring
"foo" | "bar"
tokeyof T
, the inference algorithm now infers{ foo: {}, bar: {} }
toT
. When inferringstring
tokeyof T
, the inference algorithm now infers{ [s: string]: {} }
toT
.Note that I don't have a test for the change to contravariant inference. I'd appreciate any help coming up with a test that fails without the change and passes with it.