Skip to content

Incorrect Type Inference for Array Properties in Recursive Type DefinitionsΒ #61394

Closed

Description

πŸ”Ž Search Terms

array type

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.9.0-dev.20250310#code/C4TwDgpgBA4hzAgJwDwBUB8UC8UAUAlDlmgFCmiRQDK8iqmOUA3qVO1CgKpQQAeiAHYATAM5RMeAG4BDADYBXCAC58YJBCmq0RbFi4FVXNh268BEEeMmzFKqAFE+AY0XCI3ADRQAYgsHOwACWAPaCGIZQxgC+5JTQ1EEA5oLy6Fi4cAjI6VAAZDR0OZhx4NAAShAygUFSHoy4iSlpjAV4aOZCYlAhAEYAVhCBUAD8LFAaMsJhciBQANoA0lBBglAA1hAgIQBmEgC6qpXVwXXoS-tY0VCqzNEEpVTHNXUAgkhIMnO4z6ce758QCh-OtBCEAO7hDCPCpVF4QACyMjATF+tQ8SLAwMEoIhgm8ILBkIw0IoZSgAK+PhCSAc1QAFqi4X9KSB5gByHY0unOens-ZQAD0gqglVEITkdXEwBCFI+X2xuOJHK5tIZ-JhUEx1LVvKZJ3RmJV3PVAuFoog4sllqgMpoyVSchQmMVRPxUEJeJJxt1fIFBTuQA

πŸ’» Code

type Getter<T> = () => T

type Setter<T> = {
    <U extends T>(value: (prev: T) => U): U
    <U extends T>(value: Exclude<U, Function>): U
}

type Signal<T> = Getter<T> & Setter<T>

type Reactive<T> = Signal<T> & (T extends object ? { readonly [K in keyof T]: Reactive<T[K]> } : {})

type ReactiveArray = Reactive<Array<unknown>>

type ReactiveMap = Reactive<Map<unknown, unknown>>

type ArrayForEach = ReactiveArray['forEach'] // Resolves to Array<unknown>['forEach']

type MapForEach = ReactiveMap['forEach'] // Resolves to Signal<Map<unknown, unknown>>['forEach'] & {}

πŸ™ Actual behavior

The forEach type is inferred as Array<unknown>['forEach'], retaining the original Array method signature instead of being transformed into a Signal type like Signal<Array<unknown>['forEach']> & {}. This issue affects all Array properties.

πŸ™‚ Expected behavior

For ReactiveArray, defined as Reactive<Array<unknown>>, I expect ReactiveArray['forEach'] to be Signal<Array<unknown>['forEach']> & {}, similar to how ReactiveMap['forEach'] is inferred as Signal<Map<unknown, unknown>>['forEach'] & {}, reflecting the recursive application of Reactive<T>.

Additional information about the issue

No response

Activity

  1. Andarist commented on Mar 11, 2025

    @Andarist
    Contributor

    Homomorphic mapped types that iterate directly over keyof T have special support in the compiler when they are instantiated with an array/tuple type (or intersections of those). This isn't something that can be changed. You can work around this by changing your definition to this:

    type Reactive<T> = Signal<T> &
      (T extends object ? { readonly [K in keyof T as K]: Reactive<T[K]> } : {});
  2. ahzvenol commented on Mar 11, 2025

    @ahzvenol
    Author

    It's useful, thank you for the response

  3. 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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions