Repository navigation
Inconsistent property type inference #49463
Description
Activity
RyanCavanaugh commented
on Jun 10, 2022 MemberMore actionsSimpler version:
type Template = { prop: number; } type SomeType<T> = { [Property in keyof T]?: boolean; } & { [s: string]: string }; // section A: no TS error let something: SomeType<Template> = {}; something.prop = true; something.anotherProp = 'value'; // section B: TS error // Type '{ prop: true; anotherProp: string; }' is not assignable to type 'SomeType<Template>'. // Property 'prop' is incompatible with index signature. // Type 'boolean' is not assignable to type 'string' something = { prop: true, anotherProp: 'value', };
It seems like you're trying to emulate rest index signatures, which aren't really supported. See #7765.
- addedDuplicateAn existing issue was already createdAn existing issue was already created
on Jun 10, 2022 That's very, very sad because this sounds like a very useful feature when working with types. (at least in my use cases ^.-)
So basically, in order to achieve this, I have to generate a separate file with type definitions from runtime information as relay does.
Thank you very much though.
The problem with “rest” properties is, given you have
{ [x: string]: string, foo: number }, what should the type system do when given an arbitrarystringkey that happens (at runtime) to be"foo"? That’s not something the type system can verify at compile time, so TS doesn’t even try to model this case and instead treats the index signature as universal.Note that you have to be very careful when using this pattern at runtime for the same reason - if your keys are dynamic (provided by the user, e.g.), you have to make sure they don’t overlap with the named ones.
I am not saying
{ [x: string]: string, foo: number }should work because this would obviously be bad design. But if you write it as{ [x in any string but 'foo']: string; // (pseudo syntax) foo: number; }then there would be no problem to determine the type for property
babyorfoo. (This prolly cannot be implemented in the current TS architecture)Edit: I wanted to make use of the (non-existing, because I didn't know it better) rest feature with recursion but recursion doesn't seem to work even if rest properties were supported.
Well,
{ foo: number } & { [x: string]: string }(roughly what you have in the OP) is basically the same type as{ [x: string]: string, foo: number }and neither one is safe for the above reasons, is what I was getting at. Your pseudocode above would indeed be a solution, but there's currently no way to express "any string except 'foo'" - that would require Negated Types, which, judging by the fact that PR was recently closed, looks like have been taken off the table. 😢I am not saying
{ [x: string]: string, foo: number }should work because this would obviously be bad design.Yep, I agree - but you'd be surprised how many people still want that exact type to work, and will argue vehemently with you when you try to tell them it's unsafe. It's refreshing to see someone who recognizes that. 😄
- locked as resolved and limited conversation to collaborators
on Oct 22, 2025
Section A causes no TS error in the source code below.
Section B causes a TS error in the source code below.
Both sections do the same thing and section B shouldn't yield any errors. (?)
The message "Type 'boolean' is not assignable to type 'string'" also makes no sense because
propis clearly abooleannow. (?)(?) means: I am pretty sure, but not entirely sure, as I am new to Typescript. ^^
Here is the playground.