Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cloned object with extended generic type cannot have values assigned to its existing keys #37604

Closed
cantiero opened this issue Mar 25, 2020 · 4 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@cantiero
Copy link

TypeScript Version: 3.8.3

Search Terms:
"Type 'string' cannot be used to index type"
"Type 'string' cannot be used to index type clone"

Code

type Obj = { [index: string]: any }
function f<T extends Obj>(original: T) { 
  const o1 = { ...original }
  const o2 = {} as Obj
  Object.keys(o1).forEach(k => {
    // Error: Type 'string' cannot be used to index type 'T'.
    o1[k] = 1 // this should be ok
    o2[k] = 1 // why would this be ok if the above is not?
    const a = o1[k]  // and this is fine?
  })
}

Expected behavior:
The error should not exist as:
(1) o1 has the keys as in T.
(2) T extends Obj. So even if (1) was not true, it should be ok.
It's does not seem right that the next two assignments are ok if o1[k] = 1 is not.

If this is intended behavior, like the one in #32704, I just can't make sense of it. Specially considering the similar assignments that do not raise errors.

Actual behavior:
o1[k] = 1 yelds "Type 'string' cannot be used to index type 'T'."

Playground Link:
https://www.typescriptlang.org/play/index.html#code/C4TwDgpgBA8gRgKygXigbygbQJYDsAmEAHgFxQDOwATngOYC6ZAhriFAL4BQAZgK64BjYNgD2uKNwA8AFSjFgEAuViIAfAAoRNWniYAbMtICU6KJyhQBYylBEBGFKYB0Lrdh259Hc5evBbAEyOaOxQTMrwCD6REEJOANYQIOSadkZO3FoAokwCABbq8Siq6D4W9pjx9I4OAPS1UMB52MrkeSK8evhQcNAi8WWBldWodQ0A7nls4x1djc3KvbZF2Nzz0ExwIgBu0C1QuCLAAPw+7Eac7EA

Related Issues:
#31661, #32704

@cantiero
Copy link
Author

cantiero commented Mar 26, 2020

I think I got why the TS behaves as it does above: there is no way to know what type o1[k] already has, so it is not possible to assign a number to it safely.

But I just stumbled upon another similar issue for which this explanation would not hold. This piece of code:

function f<S extends { [i: string]: any }>(t: S, s: S) {
  const r: Partial<S> = {}
  const set = Object.keys(t).reduce((prev, k) => {
    return {
      ...prev,
      [k]: (value = t[k]) => {
        if (value !== s[k] || k in r) {
          // Error: Type 'string' cannot be used to index type 'Partial<S>'.
          r[k] = value
        }
      }
    }
  }, {})
  return {set, r}
}

The expected behavior would be for the error to not exist because whatever the nature of value, it is the same as t[k]. Therefore, assigning it to r[k] does comply with the type definition for r.

Playground link: https://www.typescriptlang.org/play/index.html?ssl=16&ssc=2&pln=1&pc=1#code/FAMwrgdgxgLglgewgAhAHgMrIKYA8bYQAmAzsgN7IDacAXMiTAE5wQDmAuvQIYQCeyAL4A+ABQx6GADQNJASgrBkyKEkbIm9AArcm8bgBtMw5AF4KgpSrUwG2W+YDyAIwBW2WADoA1tj4lxOU8mbCIwKGxRUQAHEIA3GW8FUxNyK2UQmDAmFDTlfORPItjsBPT8qm8uZFE4wzBsM2QYSo5k1PKCuBAauoMG5ABCU3MSVuQAHwnkb2RWDQU8guWAehXkABU+aMaAckYWdl2VXggEW2dGsBJQ5oQ54jxm7b2dPThDY13PTuWmcfMfQav2UlmWQnKYKEMnIgjkVky2VyNxgMiYlkEQA

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Mar 27, 2020
@RyanCavanaugh
Copy link
Member

This is all intended behavior; different code will typecheck differently (this is how it's possible that some programs are OK but others aren't). One aspect is that Object.keys doesn't return keyof T; see other issues on this.

@cantiero
Copy link
Author

Thanks for answering, Ryan.

"Object.keys doesn't return keyof T". Right, that helps understanding why TS cannot check this properly. Issue #34591 looks to be similar and it includes a suggestion to iterate with for...in instead.

When you say "This is all intended behavior": Object.keys based iterators are fairly commom practice. So, it would probably make sense to have some documentation on what is exaclty the intended bahavior for these cases, why there is no type-safe way to iterate over an object with a generic set of keys and what the proper options around it look like.

"different code will typecheck differently". Yeap, amen!

@typescript-bot
Copy link
Collaborator

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

3 participants