Skip to content

Two level deep indexed access: read works, write fails #47352

Description

@quixot1c

Bug Report

Reading from a two level deep Indexed Access type works, but writing gives "Type cannot be used to index".

🔎 Search Terms

  • Indexed Access
  • Mapped Type

🕗 Version & Regression Information

  • This changed between versions 3.3.3 and 3.5.1

⏯ Playground Link

Playground link with relevant code

💻 Code

enum A {
    First = "1",
    Second = "2"
}

type Values = { [a in A]: { [b: string]: string }}
const values: Values = {} as any;

function test<T extends A>(a: T, b: string) {
    const valueA = values[a];
    values[a] = valueA;

    const valueB = values[a][b]; // indexing Values[T] with string works
    values[a][b] = valueB; // => Type 'string' cannot be used to index type 'Values[T]'    
}

🙁 Actual behavior

Indexing a type while reading works, while writing fails with the same indexer.

🙂 Expected behavior

Indexed reads and writes both work when using the same indexer.

Activity

  1. jcalz commented on Jan 9, 2022

    @jcalz
    Contributor

    I don't see what this has to do with TS4.6 or the linked PR. Your code example has the same behavior in TS4.5 and indeed all the way back to TS 3.5. I assume it's a consequence of #30769.

  2. quixot1c commented on Jan 9, 2022

    @quixot1c
    Author

    Joe Calzaretta (@jcalz) seems you are right. I previously tested with a different return type for each of the Values with

    interface Lookup {
      [A.First]: number
      [A.Second]: string
    }
    
    type Values = { [a in A]: { [b: string]: Lookup[a] }}

    This is where 4.6 did come in to play. Simplifying the problem further I must have forgotten to check if it persisted in older versions. Have updated the issue.

  3. changed the title [-]TS4.6 Two level deep indexed access of Mapped Type: read works, write fails[/-] [+]Two level deep indexed access of Mapped Type: read works, write fails[/+] on Jan 9, 2022
  4. changed the title [-]Two level deep indexed access of Mapped Type: read works, write fails[/-] [+]Two level deep indexed access: read works, write fails[/+] on Jan 9, 2022
  5. quixot1c commented on Jan 9, 2022

    @quixot1c
    Author

    As Joe Calzaretta (@jcalz) pointed out it does seem related to #30769. In particular the following:

    • Given a type variable T with a constraint C, when an indexed access T[K] occurs on the target side of a type relationship, index signatures in C are now ignored. This is because a type argument for T isn't actually required to have an index signature, it is just required to have properties with matching types.
    function f3<T extends { [key: string]: any }>(obj: T) {
        let foo = obj['foo'];
        let bar = obj['bar'];
        obj['foo'] = 123;  // Error
        obj['bar'] = 'x';  // Error
    }

    If in this case values[a] is typed as Values[T], does that mean that it in effect becomes the T with constraint C that is referred to in above quote from the PR, meaning its index signatures are ignored?

  6. RyanCavanaugh commented on Jan 11, 2022

    @RyanCavanaugh
    Member

    On read you're allowed to fall back to the constraint type, but as noted above this isn't allowed during a write

  7. quixot1c commented on Jan 11, 2022

    @quixot1c
    Author

    Ryan Cavanaugh (@RyanCavanaugh) I can't say I understand why, that may be way above my pay grade :) But it seems to me

    const valueB = values[a][b];
    values[a][b] = valueB;

    should never fail given no changes in a and b.

  8. RyanCavanaugh commented on Jan 11, 2022

    @RyanCavanaugh
    Member

    To correctly analyze that, the type of valueB would somehow have to encode that it came from the specific indices given by a and b. The type system is generally designed to track sets of values (types), not specific values or variables.

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

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions