-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Invalid return value accepted by generic indexed access type with index signature #60700
Comments
It's a design limitation. Types with indexer and known properties are inherently unsound. For this to work you'd need a type "any string, except ..", which would require negated types. |
Can you explain why? If it were just my "dubious" example I'd get it, but there's generally no trouble when all other known properties are assignable to the index signature value type. As far as I can tell this particular problem only arises when there's a type parameter involved. Here's an example of similar errors being correctly caught by the compiler. |
From my not-very-familiar-with-the-internals perspective, it seems like the issue is that the value |
Interestingly, the error is caught when the constraint is more specific: type Returns = {
a: 'a'
[key: string]: string
}
const shouldBeAnError = <K extends 'a' | 'b'>(_k: K): Returns[K] => 'oops'
// ^^^^^^
// Type 'string' is not assignable to type 'Returns[K]'.
// Type '"oops"' is not assignable to type '"a"'. |
Because the type says any This is perfectly valid code: const returns: Returns = { a: 'a' }
const keyAsString = 'a' as string
returns[keyAsString] = 'abc' When the key used to access is typed |
My mental model for this kind of compatible index signature (i.e. not the dubious example) doesn't require such a type.
I would expect these two examples to be reasoned through similarly ( type OtherType = { a?: string, b?: string }
type Returns = { a: 'a' } & OtherType
declare const returns: Returns
const key = 'a' as keyof Returns
returns[key] = 'abc'
//^^^^^^^^^^
// Type '"abc"' is not assignable to type '"a"'. type OtherType = { [key: string]: string }
type Returns = { a: 'a' } & OtherType
declare const returns: Returns
const key = 'a' as keyof Returns
returns[key] = 'abc' // no error, but should be? In both cases the |
This example correctly errors too (without any need for negated types): type OtherType = { [key: string | symbol]: string }
type Returns = { [key: string]: 'a' } & OtherType
declare const returns: Returns
declare const key: keyof Returns
returns[key] = 'abc'
//^^^^^^^^^^
// Type '"abc"' is not assignable to type '"a"'. |
Design limitations that apply in some situations may not apply in others. All these examples are different in very fundamental ways. A type parameter constrained to The implied algorithm here is that we should iterate type Returns = {
a: 'a',
b: 'b',
[key: string]: string
}
const shouldBeAnError = <K extends string>(_k: K): Returns[K] => ??? Now the only legal return of this function is |
In my opinion, the only legal return should be type Returns = {
a: 'a',
b: 'b',
}
declare const returns: Returns
const isRightfullyAnError = <K extends keyof Returns>(k: K): Returns[K] => 'a'
const isAllowed = <K extends keyof Returns>(k: K): Returns[K] => returns[k] type Returns = {
a: 'a',
b: 'b',
[key: string]: string
}
declare const returns: Returns
const shouldBeAnError = <K extends keyof Returns>(k: K): Returns[K] => 'a'
const isAllowed = <K extends keyof Returns>(k: K): Returns[K] => returns[k] I've since realized this generic example is a specific case of a more general problem though. I'll open another issue covering the general case and close this one. (I'll totally understand if that one is also considered a design limitation.) |
Closing in favor of #60703. |
🔎 Search Terms
"return type" "generic" "type parameter" "indexed access" "index signature" "assignable" "constraint"
🕗 Version & Regression Information
⏯ Playground Link
https://www.typescriptlang.org/play/?ts=5.7.2#code/C4TwDgpgBAShwFcBOA7AzlAvFA3gKCigEMAuKAciPIKgG0BrCEMtYJASxQHMBdFtzlzwBfPHgDGAe3TAoaABaSEAGwAmAIQgBBFAFEkSSUixQAPAGkoEAB7AIKVRlYduAPgAUAfXplzASjI4RFQ0WnMeLFcKSUkwNGoJaVYoACMYgFsTBSU1TR19QyR3SnI-PAB6csJqgD0AfkT0SWUIADplSS53NMl0vyhKqA6uDAAiGLjRgBooFElZUaJRoA
💻 Code
🙁 Actual behavior
The invalid return value is allowed.
🙂 Expected behavior
The invalid return value should be flagged as a type error.
Additional information about the issue
Here's another example, though this one is more dubious since the
Returns
type is nonsensical to begin with.Similar code without an index signature is correctly flagged, and so is code which uses a non-overlapping index signature. Also the expected error appears when the generic constraint is made more specific.
EDIT: It was brought to my attention in the comments that this doesn't require anything to be generic. Assigning to any indexed access of a type with an index signature can be problematic, even when everything is concrete. I would expect this to error also, for the same reason that this does.
The text was updated successfully, but these errors were encountered: