microsoft / microsoft/TypeScript

Writes to indexed access of an index signature are not sufficiently constrained

Open
#60,703 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Experimentation Needed Suggestion
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

🔎 Search Terms

"return type" "generic" "type parameter" "indexed access" "index signature" "assignable" "constraint"

🕗 Version & Regression Information
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about index signatures
⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.7.2#code/C4TwDgpgBAogHgQwLZgDbQLxQN4CgpQIBcUA5AqflANoDWEIJAzsAE4CWAdgOYC6zbLt1wBfXLgAmEAMaoEraNID2nFlAiIU6EvGRoIuZauBR6IAIwCOPKFnKUNe9HQbnetspyUmKUAPR+UF7qrKxKrOJSsvKKKmpmAEwk9lAAPmQARg6a+i4gCe52Xj6k-oEQoeEANIRM6nCQ0sAQEkA

💻 Code
type Example = {
  a: 'a'
  [key: string]: string
}

declare const example: Example
const key1: string = 'a'
example[key1] = 'not a' // no error

declare const key2: 'a' | 'b'
example[key2] = 'not a' // error, as expected
//^^^^^^^^^^^
// Type '"not a"' is not assignable to type '"a"'.
🙁 Actual behavior

The index signature allows any string value to be assigned, ignoring the narrower type of a.

🙂 Expected behavior

Both property assignments in the above code should raise errors, for the same reason.

Additional information about the issue

This is a more generalized version of #60700. @MartinJohns helped me realize that type parameters need not be involved.

Here's another pair of examples (1, 2) which I expected to be reasoned through similarly:

type WiderType = { a?: string, b?: string }
type Example = { a: 'a' } & WiderType
declare const example: Example
declare const key: keyof Example
example[key] = 'not a'
//^^^^^^^^^^
// Type '"not a"' is not assignable to type '"a"'.
type WiderType = { [key: string]: string }
type Example = { a: 'a' } & WiderType
declare const example: Example
declare const key: keyof Example
example[key] = 'not a' // no error

In both cases Example carries with it the information that the property a must have a value assignable to 'a' (as can be seen with example.a = 'not a'). But this is not enforced when assigning to dynamic property using a key whose type matches the index signature.

Here's another example which does produce the error I expect:

type WiderType = { [key: string | symbol]: string }
type Example = { [key: string]: 'a' } & WiderType

declare const example: Example
declare const key: string | symbol
example[key] = 'not a' // error, as expected
//^^^^^^^^^^
// Type '"not a"' is not assignable to type '"a"'.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked TypeScript Playground examples and compare indexed assignments through string keys with direct property assignments. Trace how the type checker determines the writable type for indexed access, then add coverage showing that narrower declared properties are enforced; done means both reported examples reject "not a" while the existing valid cases remain accepted.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.