Consider the following code, which uses TypeScript language features introduced in v2.8 (conditional types):
type P<TObject, TPropertySuperType> = {
[K in keyof TObject]: TObject[K] extends TPropertySuperType ? K : never;
}[keyof TObject];
function g<
T,
K extends keyof Pick<T, P<T, string>>
>(obj: T, prop: K): void { }
class C {
public alpha: string;
public beta: number;
public f(): void {
g(this, "alpha"); // <-- does not compile!
g(this, "beta");
g<C, "alpha">(this, "alpha");
g<C, "beta">(this, "beta");
g(new C(), "alpha");
g(new C(), "beta");
this.g2("alpha");
this.g2("beta");
this.g2<"alpha">("alpha");
this.g2<"beta">("beta");
}
public g2<
K extends keyof Pick<C, P<C, string>>
>(prop: K) { }
}
The idea behind the type P is that it selects the names of the properties of TObject that satisfy the constraint that the type of the property extends TPropertySuperType. The functions g and g2 then use the type P in a type parameter constraint, such that:
- You can only call
gwhen thepropparameter is the name of aextends string-typed property ofobj - You can only call
g2when thepropparameter is the name of aextends string-typed property ofC.
Here, because C.alpha is of type string and C.beta is of type number, I would expect all five invocations of g/g2 with prop === "alpha" to compile, and all five invocations with prop === "beta" not to compile.
However, the invocation g(this, "alpha") does not compile, as you can see if you paste this code into the TypeScript playground. The error is:
Argument of type '"alpha"' is not assignable to parameter of type 'this[keyof this] extends string ? keyof this : never'.
Why does this particular invocation fail? I'm guessing it has something to do with how TypeScript infers the type of this, but the details are fuzzy to me.
It's hard to tell without looking at the compiler sources, but one thing is clear:
thisinsideCdoes not have typeC, based on the observation that error goes away if you add type assertionthis as C:The type
this[keyof this]spelled out in the error message in place forTObject[K]hints atthisbeing "polymorphic this" type - the type that represents all possible subtypes ofC.I don't know what causes
thisto be treated as polymorphic here (maybe it's always assumed to be polymorphic), but it has an unfortunate consequence that set of its keys is not statically known.Although it can be proven that even with polymorphic this,
"alpha"will always be among its keys andthis["alpha"]type will be compatible withstring, as it's declared inC, the compiler does not follow the necessary logic, and just plays it safe - "if it's not statically known it's incompatible".