Skip to content

Incorrect generic binding of intersection of array types #61907

Open
@jsalvata

Description

@jsalvata

🔎 Search Terms

array intersection generic
array intersection binding
intersection of array types
intersection of array types is array of intersection of element types

🕗 Version & Regression Information

  • This fails in all versions I tried in Playground: 3.3.3333, 5.8.3, 5.9.0-dev.20250620, and others

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.9.0-dev.20250620#code/C4TwDgpgBAGlC8UDOwBOBLAdgcygHykwFcBbAIwlQG4AoG0SKAQQWdVQEMQAeGAPlr1w0AMpos2FohYAyNpx5w5KDDgF0IADzAB7VMCgAzIpgDGwdDsxGAFBwBcUMaskBKKAG8aUKKasooTVYOAG0ABgBdKigAehjkcRx8KBMAEwhDLAhU7yh0QygbAEJNd1QIYCJUazDaH3LK6sCAOgAbCBxgAAtaAF8NbT0DYzMLKyhsO0dnCSZ3Lx8-TACgxA5mzMxUmxsAfVKEPig0IghXOryC4oOGqpqL26bNNo7sbr6aIA

💻 Code

type X = string | number;

type A = Array<X>;

type StringA = A & Array<X & string>;

export function f(a: StringA) {
  const x = a[0]; // string | undefined
  if (!x) return 0;
  return x.length;
}

export function g(a: StringA) {
  const x = a.find((_x) => true);
  if (!x) return 0;
  return x.length; // error
}

🙁 Actual behavior

The type of x in the last line of g is X, and thus a type error is reported.

🙂 Expected behavior

The type of x in the last line of g should be string, just like in f, and there should be no type errors.

Additional information about the issue

Why would you ever do that?

Stripe object types have many properties which can either be string or some other type, depending on whether that property was listed in expand when pulling the object.

I am trying to constrain some of those types to either option. That is: knowing exactly what I listed in expand, return a type which precisely conveys what some of those properties will be. This saves me a lot of type checking / constraining elsewhere. For example:

export type ScheduleWithoutPrices = Stripe.SubscriptionSchedule & {
  phases: Array<
    Stripe.SubscriptionSchedule.Phase & {
      items: Array<
        Stripe.SubscriptionSchedule.Phase.Item & {
          price: string;
        }
      >;
    }
  >;
};

export type SubscriptionWithTestClockAndSchedule = Stripe.Subscription & {
  test_clock?: Stripe.TestHelpers.TestClock;
} & {
  schedule?: ScheduleWithoutPrices;
};

There may be better solutions to this, possibly using a generic type for the expands option, but I don't want to redefine the whole library, just an ad-hoc fix for the occasion.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions