-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Using type alias for record prevents compiler from inferring generic function argument correctly #54000
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
Comments
My educated guess is that:
This experiment PR seems a little bit related to this kind of a problem: #47898 |
The change between origin/release-4.2 and origin/release-4.3 occurred at 89a171e. |
Based on the bisected commit... this works π€£: function foo<T>(record: { [key: string]: T }, entity: T) {}
type StringArrayRecord = { [key: string]: string[] };
function test() {
const working: { [key: string]: string[] } = {};
foo(working, []); // compiles
const broken: StringArrayRecord = {};
foo(broken, []); // this also compiles
} |
Annotated function test() {
const working: Record<string, number[]> = {};
// Candidates:
// - Same-alias-instantiation match Record<string, T> and Record<string, number[]>, recur ->
// - Bare match between Record<string, T> and Record<string, number[]>
// - Bare match from T and never[]
// ===
// ---> Two equal bare matches, subtype reduce between never[] and number[], T = number[]
foo(working, []); // Compiles
const broken: StringArrayRecord = {};
// Candidates:
// - Mapped type inference from StringArrayRecord and Record<string, number[]>
// - Bare match from T and never[]
// ===
// -----> Bare match wins on priority grounds, T = never[]
foo(broken, []); // Does not compile
} If I had to pick one to be "wrong" here, it would be the first one. An inference from Given the choice of having these be different or equally annoying, I think the former is preferable absent more concrete use cases. The other option available would be to be a bit smarter about how to deal with |
Just to recap... when we match |
π― |
Thanks for looking at this so promptly, folks! Really impressive! I agree that being smarter about type StringOrNumberRecord = Record<string, string | number>;
function test() {
const working: Record<string, string | number> = {};
foo(working, "string"); // Compiles
const broken: StringOrNumberRecord = {};
foo(broken, "string"); // Does not compile
} I worked around the issue in our codebase by simply replacing the use of a bunch of the type aliases with the actual Thanks again! |
As an experiment, I've opened a PR that avoids assigning this lower inference priority for certain mapped types (with primitive type parameter constraints): #54006 |
This issue has been marked as 'Not a Defect' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
Bug Report
π Search Terms
Record, type alias, infer, parameter, generic
π Version & Regression Information
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
When using a type alias (
StringArrayRecord
in the example above), I cannot pass[]
into functionfoo
as the compiler can't infer that this is meant to be an array of strings. But if instead of the type alias I use the fullRecord
definition, it does seem to infer it correctly, so I can pass in[]
.π Expected behavior
I'd expect the same behaviour in both cases. i.e. whether I use the type alias or the
Record
declaration I'd expect it either to compile or not compile (ideally compile of course). The issue in my opinion is that the introduction of a type alias seems to stop the compiler being able to infer the type.Asked originally on StackOverflow: https://stackoverflow.com/questions/76090127/typescript-fails-to-infer-type-correctly-when-type-alias-used. There's a bit more info there showing that this doesn't seem to be related to the fact that the generic type parameter is an array.
Thank you!
The text was updated successfully, but these errors were encountered: