Skip to content

Contextual narrowing fails inside thunked argument when T is inferred from a separate argument #63511

Description

@johanrd

🔎 Search Terms

thunk inference contextual narrowing
arrow function return contextual typing
nested array literal widening
generic inference across arguments
callback return literal narrowing
NoInfer (unrelated — same bug without it)

🕗 Version & Regression Information

  • This is the behavior in every version I tried (4.9.5, 5.5.4, 5.9.3, 6.0.0-dev.20260416), and I reviewed the FAQ.

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=6.0.0-dev.20260416#code/C4TwDgpgBAwg9gGzgJygXigcmRAJpqAHywCMEBXCTAbgChRJYBDZZASwmQB4AVAPnRQA3lAD6o4AH4AXFAAUAD1k8AlOgEA3OG1xQAvnVq4IAYwQtoAM3IA7E8DZwbUXGxz3efOSdkwW7Tk8AGig4MGAAZ1kRE0QUKKgcJlwnBBAoHgBtAF19FWU6YzMLKGs7BycoYAALWwBrPE9vX38Obn4QsMjZOTU0ARi45ASklJs0jJy8gtpaWJsI4CgTQRtyBAQoJgioeps4AHdnbeZWNq54JGQ+Q1d3YG8QwauEzOw8TFy9FWooP-+AVAAPRAqCAUHIoDZ-IcEjxBJcUPRajYGrhHvI+gI5M94rI3jh8F8VD8-iCoIAZcigi3YNgA5lN9ktthE2LSoWRoMA4IkIMlUukEcgckA

💻 Code

type Color = 'red' | 'blue';
type Carrier<T> = { __t?: (x: T) => void };

declare function direct<T>(c: Carrier<T>, opts: { colors: readonly T[] }): T;
declare function thunked<T>(c: Carrier<T>, opts: () => { colors: readonly T[] }): T;

const c = null as unknown as Carrier<Color>;

direct(c, { colors: ['red'] });           // ✅ narrows: T = Color
thunked(c, () => ({ colors: ['red'] }));  // ❌ string[] not assignable to readonly Color[]

🙁 Actual behavior

T is inferred as Color from Carrier<T> in both calls. In the thunked form the nested array literal ['red'] widens to string[] before contextual typing reaches it from readonly T[], producing Type 'string' is not assignable to type 'Color'.

🙂 Expected behavior

The thunked form narrows the same way as direct: ['red'] is contextually typed against readonly Color[] and the call type-checks. The two forms differ only in whether the options are bound directly or via an arrow function — the inferred T is the same.

Additional information about the issue

The pattern matters for reactive-resource wrapper libraries (e.g. glimmer-apollo) that require args to be wrapped in a function so they can re-evaluate when tracked state changes. The thunk shape is idiomatic; the inference asymmetry forces consumers to scatter as const casts across nested literals.

Possibly related, but verified not the same bug:

Activity

  1. changed the title [-]Contextual narrowing of nested array literal fails inside arrow returning a tuple[/-] [+]Contextual narrowing fails inside thunked argument when T is inferred from a separate argument[/+] on May 28, 2026
  2. MartinJohns commented on May 28, 2026

    @MartinJohns
    Contributor

    Duplicate of #241.

  3. johanrd commented on May 28, 2026

    @johanrd
    Author
  4. locked as resolved and limited conversation to collaborators on Jul 27, 2026
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions