Skip to content

Context sensitive inference fails with class instance #62380

Description

@Mike-Dax

🔎 Search Terms

"intra expression inference class", "context sensitive class inference"

🕗 Version & Regression Information

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

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/MYGwhgzhAEBiD28A8AVAfNA3gWAFDWmHgDsIAXAJwFdgz4KAKAByoCMQBLYaD44gUwoAuaCgCUWAL55puPKEgwAQmAoBhcFFQYc+QiXLVa9Zm07cwwYPyj0RDcmDL8R46AF4MrRCH5hiEpiysni6BAD04aIAFhwwcdAAtgCe0JZkVGAg0EwU8AAmNGQcJNBUEPyEkPwANNDEqnkA7vz50PnwTcQAdHgRUQAGrKoQAzww-mkUFGCp8ABmhJrxpGT+1jBNHGTRadAA5vwCFFw8xPOCFK3Q83mJfdCR+sTOAB5k0As5VwBuJeU3KjEWglYhVEAgCC9PQKKDQNQkNa8QRINR1WA6B4EIirIx0RhYgg5MynWivERowkEFjsU7zRCwdL0ZL2MkUiSeOA1KnE2ncYYUCAiFTqZZIDEAbQAuty9ARArICCEYQYPjikccPPV+E14YiwMiCXLoAAWABMsqJhDIrw8GAEuoQ8AYmDSpBawmttskYktRIlDugIo0igca2cduglCo-DEMsJTytSYIAD0APwJqKOCMJIEAa2InWIDzEMjwoQeTxQ0UquXg7H4iWg-FecTIm22uzA0AgvH2VHAFDOjmBlS+O0qsJg0U6-B+gmh2OWepeBuOqPRmONOMMRRMPJp5i9FL9VsPdIZTIoLOgDDZ8I5GFgp6J5-5qmFqhDWgxL4VD2VbFVWeDVBAAdU7eAqDIABBPh4HDUEtUDBFV0NBhCXNF8yUjQMnRdN0IA9EQcJ9F9A2DZYwycSpOWjWMX0TZMiXTTMe3DSpc2IAsixLcsVVWEC13Azs4MLRDSncbVdVQ0CjStLDCTvG0RGIKhElYQRH2kuBEAI-wiMEEibWgMjHiiSYWyYcxtijZImEqfxxKcJDShwmsrhuDhXhsKMa3GCAYzONIISqCoIDqVhoPGYgAHIPlydIuCyG56CSVIYIABQASTKCowv4QkKK-Kjs1ojB6N9NjmJYjNjSeMrxl05BXQM4j6nUzSh0kNA+NwZUwnM6AAAkwD+Yh9j2eYr1SeYgRBUoth2ep4CmftEiOdt2ngGw4o+GsQCYB4pxXOSNzgLcrR3aNjHkpM32PeEX2pEluHpeBGVum9lPJB9I2fA9XugAVPvxb7tMoxRxTQP8pAA-igME9VhKHKSUP1dDMItQkcM5PC9Na90jK9UyquNBhtOK0VQ0aujqAY6qauY1j6qzDimvzQsuj6gbKyiGCbhmwFgWKRbOz2QpEhSNb1M26AAH0OhseXoCaeg82O5dZJR86MSwHHVTxfdjRevlHspE3eSPd7QeZVkVL+zkActh6QZm+xJZSVS5y0yNIZ-GHCX-PRAOecghMNCCdgAEXU6W0Z1U6UYw41FO3Ey8cT-DCcMz1SLJq1PdSTPdX9iBqIjOmYwLokmKZmqWatBr2YSJ0kBz9q1I0wRTN6vRSzkPQnhnFp5yHDhFgndo49SVR1rluI9pyRRWjqWylYgJe1YoDWBPD5HI87MC8gmmCTjIYudO1zHU+x9PbRL5r9KJvOTLIpTKcTsuK-KqN6Zr-o9dmZ1SbmzGiHNuJc2LP3Msg9BoXwckGVQAB5VgAArfgtBtBakGgQSwGwIB2FvGVVw2lvDwF8P4eGg9AECyuP2Qcnx0GYI+AkXgFxpjXCIJw2gIBkiLiWIoJOhpdaXSJNdI2d0zxA3vBbZMD0bbu1vLI7Szt5FAwFJ+CgqCMFYN-EHOGIcEZhzVBjTUCcZJmMECnBSd8roZ3tFnAmhF2r5xfK1KwNhCGelphVemplGJRCZo3WuYCcwwDbh3YmXcuq9x5kAA

💻 Code

class Foo<T> {
  constructor(public inner: T) {}
}

class BarClass<T> {
  constructor(public accessor: (state: T) => boolean) {}
}

{
  // This is my actual production use case, narrowed down.
  // `bars` is an array of class instances with a generic inferred from
  // context of previous function calls.
  class Container<C, F> {
    constructor(
      public ctx: C,
      public fooFactory: (ctx: C) => F,
      public bars: BarClass<F>[],
    ) {}
  }

  const container = new Container(
    42,
    ctx => new Foo({ answer: ctx }),
    [new BarClass(state => true)],
    //            ^?
    // state is unknown
  )
}

{
  // The problem exists with a singular instance of the class however.
  class Container<C, F> {
    constructor(
      public ctx: C,
      public fooFactory: (ctx: C) => F,
      public bar: BarClass<F>,
    ) {}
  }

  const containerWithoutAnnotation = new Container(
    42,
    ctx => new Foo({ answer: ctx }),
    new BarClass(state => true),
    //           ^?
    // state is unknown
  )

  const containerWithAnnotation = new Container(
    42,
    (ctx: number) => new Foo({ answer: ctx }), // an explicit type annotation on ctx here fixes the issue
    new BarClass(state => true), //               in all cases, but isn't practical for my API use case
    //           ^?
    // state is Foo<{ answer: number }>
  )
}

{
  // Having a factory function with no arguments doesn't help
  class Container<C, F> {
    constructor(
      public ctx: C,
      public fooFactory: (ctx: C) => F,
      public barFactory: () => BarClass<F>,
    ) {}
  }

  const container = new Container(
    42,
    ctx => new Foo({ answer: ctx }),
    () => new BarClass(state => true),
    //                 ^?
    // state is unknown
  )
}

{
  // A factory function with a dummy argument _does_ work
  class Container<C, F> {
    constructor(
      public ctx: C,
      public fooFactory: (ctx: C) => F,
      public barFactory: (dummy: never) => BarClass<F>,
    ) {}
  }

  const containerWithDummy = new Container(
    42,
    ctx => new Foo({ answer: ctx }),
    dummy => new BarClass(state => true),
    //                    ^?
    // state is Foo<{ answer: number }>
  )

  // however if the dummy argument isn't passed, it doesn't work
  const containerWithWrongArity = new Container(
    42,
    ctx => new Foo({ answer: ctx }),
    () => new BarClass(state => true),
    //                 ^?
    // state is unknown
  )
}

{
  type BarObject<T> = {
    accessor: (state: T) => boolean
  }

  // A regular object is inferred correctly.
  class Container<C, F> {
    constructor(
      public ctx: C,
      public fooFactory: (ctx: C) => F,
      public bar: BarObject<F>,
    ) {}
  }

  const container = new Container(
    42,
    ctx => new Foo({ answer: ctx }),
    { accessor: state => true },
    //          ^?
    // state is Foo<{ answer: number }>
  )
}

🙁 Actual behavior

The BarClass type isn't inferred correctly.

🙂 Expected behavior

I think I'd expect BarClass to be inferred in all those cases? Except perhaps when the dummy argument is in the function signature, but the passed function has the wrong arity.

Additional information about the issue

Under the umbrella of #47599

This PR+comment feels close #54183 (review)

Activity

  1. Andarist commented on Sep 2, 2025

    @Andarist
    Contributor

    Technically, it's not about intra expression inferences - that term refers to situations in which the producer and a consumer of an inference appear in the same argument expression. Your consumers and producers are spread across different arguments.

  2. changed the title [-]Intra expression inference fails with class instance[/-] [+]Context sensitive inference fails with class instance[/+] on Sep 2, 2025
  3. RyanCavanaugh commented on Sep 2, 2025

    @RyanCavanaugh
    Member

    Agreed; no amount of #47599 would fix this. #30134 is the only fix when you have places like this where inference would have to visit different levels of the tree in arbitrary different orders.

  4. typescript-bot commented on Sep 5, 2025

    @typescript-bot
    Contributor

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

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

    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