question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

String index into a generic type having a string index signature isn't assignable to indexed access type

See original GitHub issue

Bug Report

🔎 Search Terms

string index signature; indexed access; generic; 2322; not assignable

🕗 Version & Regression Information

This seems to be there all the way back to TS3.5, maybe introduced by #30769, although I’m not sure I understand why.

-This changed between versions 3.3.3 and 3.5.1

⏯ Playground Link

Playground link with relevant code

💻 Code

interface Foo {
    a: { [k: string]: string | undefined };
    b: { [k: string]: number | undefined };
    c: { [k: string]: boolean | undefined };
}
function get<K extends keyof Foo>(foo: Foo, k: K, x: string): Foo[K][string] {
    return foo[k][x]; // error!
    // Type 'string | number | boolean | undefined' is not assignable to type 'Foo[K][string]'.
}

🙁 Actual behavior

Reading a property with a string key from a Foo[K] object does not produce a value of type Foo[K][string], even in a non-widening position.

🙂 Expected behavior

Indexing into a Foo[K] value with a string key should produce a property of type Foo[K][string] if such a type is in a non-widening position.


This might be similar to #31661 and #33181 but I can’t tell.

Note that numeric index signatures work as expected:

interface Foo {
    a: { [k: number]: string | undefined };
    b: { [k: number]: number | undefined };
    c: { [k: number]: boolean | undefined };
}
function get<K extends keyof Foo>(foo: Foo, k: K, x: number): Foo[K][number] {
    return foo[k][x]; // okay    
}

And known literal keys work as expected:

interface Foo {
    a: { x: string | undefined };
    b: { x: number | undefined };
    c: { x: boolean | undefined };
}
function get<K extends keyof Foo>(foo: Foo, k: K, x: "x"): Foo[K]["x"] {
    return foo[k][x]; // okay    
}

But string index signatures (and symbol also) fail for some reason.

This can be worked around easily enough:

function get<K extends keyof Foo>(foo: Foo, k: K, x: keyof Foo[K]): Foo[K][keyof Foo[K]] {
    return foo[k][x]; // okay
}

But I’m wondering what’s going on.

Playground link with above examples

(note to self: go back to this SO answer if I acquire more cluefulness here)

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:1
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
RyanCavanaughcommented, Oct 10, 2022

That’s a widening, the same as you’d see in m here:

    function get2<K extends keyof Foo>(foo: Foo, k: K, x: number) {
        const fk: Foo[K] = foo[k]
        let m = fk[x];
    }
1reaction
jcalzcommented, Oct 11, 2022

Okay, then I guess I meant “in a non-widening position” instead of “contextually” or something. Unless that’s wrong too.

Read more comments on GitHub >

github_iconTop Results From Across the Web

`Type 'string' cannot be used to index type 'T'` when indexing a ...
The error is correct. You're not extending objects only having string typed properties, you're extending objects where the properties extend ...
Read more >
typescript - Combining generics with index type - Stack Overflow
Let's talk about index signature types and mapped types. They have similar syntax and do similar-ish things, but they're not the same.
Read more >
Index Signatures in TypeScript - Dmitri Pavlutin
Property 'baseSalary' is incompatible with index signature. Type 'string' is not assignable to type 'number'.
Read more >
Index Signatures - TypeScript Deep Dive - Gitbook
Index Signatures. An Object in JavaScript (and hence TypeScript) can be accessed with a string to hold a reference to any other JavaScript...
Read more >
Mastering TypeScript mapped types - LogRocket Blog
Indexed access types in TypeScript; Index signatures in TypeScript; Using union types ... Type 'string' is not assignable to type 'number'.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found