Skip to content
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

Incomplete inference of generic union type over array #60893

Closed
adriantrunzo opened this issue Dec 31, 2024 · 3 comments
Closed

Incomplete inference of generic union type over array #60893

adriantrunzo opened this issue Dec 31, 2024 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@adriantrunzo
Copy link

πŸ”Ž Search Terms

generic, array, union, inference

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about generics

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.7.2#code/C4TwDgpgBAaghgGwK7QLxQHZILYCMIBOUAPlAM7AECWGA5gNwCwAUC6JFAEID2AHhABN4yCAB4AKlAi9gEDALKxEKAHxR0AbxZQoAN2UQAXFHFNmAXzNtw0AAoFuYMhKky5CpSLWbtUXHwgyAH5jAgg4AW4MBBAuAKEDCRUAbQBdMx0yCAQIAGNZAWMAOW4ASQwAM0Iks3MWFlyoiigomKKcfAIefkV0ZN8NPQNjAEYocwAaAaGRYwAmcZZUqDhFRowKK2Z15uwqfgFuwPUofuYdQf1ZqDHJ6auUYwAiOafF5mXVqB3gLZ+oCoYE4uaSyeSKYSqAAUg38PQm5GyeQK42M9kcznEKgAlOo1Fpzt8mtwcgA6BDcWhQuGBBFZHL5QTY2r1ZiAmF+AJkYytEDtPCEI5kOlIxmFG7jZlQAD00qgAHkANIIyRURQAAzGpDm6pY7NhXOMewOQpFDIKo0l9BlcoAogQHAQVTYoAByF5PV1QNWYbjAFZkMhUWgYOC4HJQYDcSMu10jV16jCiKHsCDcCpQY2CIXY5JYAUEVLJJ4PCBPVIqDk07mZ-bZrlm5GCS3mKWyhXKkzejVaqAe9VAA

πŸ’» Code

type Value = number | string;

type BoxedValue<T extends Value> = {
  value: T;
};

type Props<T extends Value> = {
  boxes?: readonly BoxedValue<T>[];
  selected: NoInfer<T>;
}

const onlyNumberBoxes = [
  { value: 1 },
  { value: 2 }
] as const;

const mixedBoxes = [
  { value: 1 },
  { value: "2" }
] as const;

const fn = <T extends Value>({ boxes, selected }: Props<T>) => {
  console.log(boxes, selected);
}

fn({ boxes: onlyNumberBoxes, selected: 1 }); // OK, T is `1 | 2`
fn({ boxes: mixedBoxes, selected: 1 }); // Error, Type '"2"' is not assignable to type '1'
fn<(typeof mixedBoxes)[number]["value"]>({ boxes: mixedBoxes, selected: 1 }); // OK, T is `1 | "2"`

πŸ™ Actual behavior

Typescript can't infer the type T in the second call to fn. Instead the following error occurs:

Type 'readonly [{ readonly value: 1; }, { readonly value: "2"; }]' is not assignable to type 'readonly BoxedValue<1>[]'.
  Type '{ readonly value: 1; } | { readonly value: "2"; }' is not assignable to type 'BoxedValue<1>'.
    Type '{ readonly value: "2"; }' is not assignable to type 'BoxedValue<1>'.
      Types of property 'value' are incompatible.
        Type '"2"' is not assignable to type '1'.(2322)

Even though T can be either a number or a string, the compiler seems hung up on the base type of the first value it finds. The fact that the compiler can correctly infer 1 | 2 in the first call, and is fine with the explicit annotation of (typeof mixedBoxes)[number]["value"] in the third call, makes me feel like the error for the second call is unexpected behavior. The intended code is valid (e.g. the third call), but compiler can't get there on inference alone. The same issue occurs if you remove the as const, the error just references number and string instead.

πŸ™‚ Expected behavior

No error on the second call to fn; the type T is correctly inferred as 1 | "2".

Additional information about the issue

Perhaps I am encountering a limitation on generic inference? I suppose union types could be arbitrarily large.

My goal in the example above is to restrict the accepted type for selected to the actual types given in the boxed values. I came up with a workaround that exhibits the correct behavior, but is not as intuitive: https://www.typescriptlang.org/play/?#code/C4TwDgpgBAaghgGwK7QLxQHZILYCMIBOUAPlAM7AECWGA5gNwCwAUC6JFAEID2AHhABN4yNFADeLKFABuiFAC5YciE2YBfVW3DQe-MlHQEIcAdwwIQXPoOEoA2gF1NzdtACSGAGaEjQ5QB4AFSgIXmAIDAF9XQgyAD4DKEC7LDxCBzsAIlkRTKcWLQ4ABQJuMDIgkLCIqKs9BPQJZilcazIAfkVA1SkyCAQIAGNwgUUPbwJfWwgguNU1AuZBswooMwsAORx8Ahj9dDtJcRllRQBGKDUAGiOxE5FFACZLlgcoOH1ljApnL9XsKj8AR7RKHZrHHIKKAXa63e5QzKPTIvZhvD5QP7AX4rYBQTwYRKVULhSLRNpxAAUd1aeiu5H6QxGl0UJTKFUCcQAlAYEk0pH9uAMAHQIbi0Ck02J0voDYaCTnzRb4qlQSVkRTrEBbNK7NrShly0bQy4KqAAejNUAA8gBpOnBKj6AAGF1IjydLGV1LaigBQL2+tlI3OJvo5stFutdqSUEdUBdJCgiMyHuY+P8FNc3E8UD9gj2nMp3r0vsB+b19KDghDalNkdt9tjztdSaRTqAA

If there is a limitation here around generic inference, maybe the error message can be improved? Again, since T extends number | string, it's not clear why the error occurs.

@jcalz
Copy link
Contributor

jcalz commented Dec 31, 2024

It's a feature, not a bug. You're looking for something like #44312.

@adriantrunzo
Copy link
Author

It's a feature, not a bug. You're looking for something like #44312.

Thanks for the links, you've addressed my issue.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jan 2, 2025
@typescript-bot
Copy link
Collaborator

This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Jan 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants