Open TheMrZZ opened 3 years ago
This is because the array is a stronger inference candidate than the record, so as soon as the array is non-empty, the type parameter gets instantiated to the types that can be inferred from its contents. The completions behavior you expect seems so obvious, but it’s actually really tricky to make it work. This is very similar to #36556 though, where we got reasonably good results, so maaaaybe that approach can be applied here.
Thanks for you complete and helpful answer! I understand that it's complicated to implement this feature properly, and event though I'd like to have that, I get that it will maybe never get implemented. It looks like a complicated problem, so no worries!
Stumbled upon this... In case someone wants a workaround...
interface Recipe<Self> {
quantities: Record<string, number>
allergens?: Self extends { quantities: infer Q } ? (keyof Q)[] : never
}
function createRecipe<R extends Recipe<R>>(recipe: R) {}
createRecipe({
quantities: {
eggs: 1,
flour: 2,
},
allergens: [""]
})
Hell wait much simpler workaround...
interface Recipe<I extends string> {
quantities: Record<I, number>
allergens?: NoInfer<I>[]
}
type NoInfer<T> = [T][T extends any ? 0 : never]
function createRecipe<I extends string>(recipe: Recipe<I>) {}
createRecipe({
quantities: {
eggs: 1,
flour: 2,
},
allergens: [""]
})
This workaround reverses the problem. Using a normal array, the array has the priority. It means that, if you add a property to the object, the object will error out, and you won't have autocompletion on the array. However, adding a string to the array will error the object out, and you will have autocompletion on the object properties.
Using your workaround, the object gets the priority. It means that, if you add a property to the object, the array will error out, and you will have autocompletion on the array strings. However, adding a string to the array will error the array out, and you won't have autocompletion on the object properties.
What should happen would be that changing one will error out the other, and autocompletion would always be provided. That currently works for objects:
Ah my bad. Here's another workaround. You do get an error that property sugar
is missing (so the types are correct) but there's no autocomplete, I suspect that's because of #44428 bug.
TypeScript Version: 4.1.2
Search Terms: Autocompletion, incorrect, values, inference, generic, array, object, keys
Summary: When an interface/a type has an object with generic keys, and an array of those keys, the array values cannot be infered from the object keys.
Code
Expected behavior: Here, when trying to give a value to
allergens
, the autocompletion should show"eggs" | "flour"
.Actual behavior: The autocompletion doesn't find anything.
Notes: The other way is working: you can fill the array first, then the object keys will autocomplete - but this rarely make sense to write things that way.
Failed workarounds: This bug is still present, even when:
allergens
is optionnalkeyof this['quantities']
instead ofINGREDIENTS[]