Repository navigation
this index with unions outputs intersections?ย #49393
Description
Activity
This is by design and has nothing to do with
this. The reason you get intersections is:interface I { a: string, b: number }; const v: I = { a: "foo", b: 812 }; const k = Math.random() >= 0.5 ? 'a': 'b'; // note: the type being assigned to below is I['a' | 'b'] v[k] = "bar"; // error - only 50/50 chance this is type-correct
The only way the above could be typesafe is you could somehow come up with a value of type
string & number.See #30769.
It has something to do with
this, otherwiseTest['a' | 'b']andthis['a' | 'b']would behave the same. I suspect the difference is thatTestis a specific type, soTest['a' | 'b']is resolved eagerly tostring | numberbefore the compiler is even aware of an assignment. On the other hand,thisis implemented as an implicit generic type parameter, and sothis['a' | 'b']stays as an indexed access type, and when you try to assign to such a type you get the intersection restriction as described in #30769.Reacted by Bruce PascoeAh, I didn't realize the case with a concrete type is different. I should know by now that "TypeScript" and "consistency" are two words that don't belong in the same sentence. ๐
Reacted by whzx5bybI see. It makes sense to me now. But the inconsistency is confusing.
The original issue I met is actually a little bit different. It may or may not be the same issue as this one I posted.
class Base { numFnWithThis<T extends NumKeys<this>>(key: T) {} } class Sub extends Base { a!: number; b!: string; numFnWithConcrete<T extends NumKeys<Sub>>(key: T) {} test() { this.numFnWithThis('a'); // error: Argument of type 'string' is not assignable to parameter of type 'NumKeys<this>'.(2345) this.numFnWithConcrete('a'); // ok } } type NumKeys<T extends object> = { [K in keyof T]: T[K] extends number ? K : never; }[keyof T];
I have a method in base class that accepts certain types of property keys of the concrete class. The type filter helper (
NumKeys<T>) works well with a concrete type, but doesn't work withthistype.- addedWorking as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
on Jun 6, 2022 RyanCavanaugh commented
on Jun 6, 2022 MemberMore actionsIt's consistent, just consistent with a different set of rules than you were expecting ๐
The general way to think of it is that expressions involving
thisare always deferred within the class, to prevent unsoundnesses from occurring when a derived class has a more-specific type of some property than its base class.The second example is tricky because TS is warning you about a condition that, by construction, can't actually occur in this sample. It's not safe in general to eagerly resolve
NumKeysto some union in this code, because we don't "know" thatNumKeys<some speculative derived type>is a supertype ofNumKeys<Sub>. However, the combination ofNumKeysbeing effectively contravariant with the fact that any derived class ofSubhas to be a subtype means that this example should be legal, we just don't have the mechanics in place to detect this combination of facts and identify it as actually-OK.typescript-bot commented
on Jun 8, 2022 ContributorMore actionsThis issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
- locked as resolved and limited conversation to collaborators
on Oct 22, 2025
Bug Report
๐ Search Terms
๐ Version & Regression Information
This changed between versions 3.3.3 and 3.5.1, and it keeps the behavior since then to the current latest version (at least 4.7.3).
โฏ Playground Link
Playground link with relevant code
๐ป Code
๐ Actual behavior
๐ Expected behavior
I expect
this['a' | 'b']to bestring | numberin this example, thus accepting the assignment of''to it, just likeTest['a' | 'b'].