Skip to content

Incomplete inference of generic union type over arrayΒ #60893

Closed as not planned
Closed as not planned

Description

πŸ”Ž 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.

Activity

  1. jcalz commented on Dec 31, 2024

    @jcalz
    Contributor

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

  2. adriantrunzo commented on Jan 1, 2025

    @adriantrunzo
    Author

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

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

  3. typescript-bot commented on Jan 5, 2025

    @typescript-bot
    Contributor

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

  4. locked as resolved and limited conversation to collaborators on Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions