考虑以下代码,该代码使用TypeScript语言功能introduced in v2.8(条件类型):
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) { }
}
类型P
背后的想法是,它选择TObject
的属性的名称,这些属性满足以下条件:该属性扩展了TPropertySuperType
。然后,函数g
和g2
在类型参数约束中使用类型P
,例如:
g
参数是prop
类型的extends string
类型的属性的名称时,才能调用obj
g2
参数是prop
类型extends string
类型的属性的名称时,才能调用C
。在这里,因为C.alpha
的类型为string
,而C.beta
的类型为number
,所以我希望g
/ {{1 }},使用g2
进行编译,而所有五个使用prop === "alpha"
的调用 not 进行编译。
但是,调用prop === "beta"
不会 进行编译,因为您可以看到是否将此代码粘贴到the TypeScript playground中。错误是:
g(this, "alpha")
为什么这个特定的调用失败?我猜想这与TypeScript如何推断Argument of type '"alpha"' is not assignable to parameter of type 'this[keyof this] extends string ? keyof this : never'.
的类型有关,但是细节对我来说很模糊。
答案 0 :(得分:2)
我同意arthem,最可能的罪魁祸首是多态this
。虽然很明显this
的类型将是多态的this
。尽管您可以肯定地说C['alpha']
的类型为string
,但对于this['alpha']
却不能这样说,您只能说this['alpha'] extends string
这是一个更为复杂的关系供编译器遵循。不确定这种类比是否有帮助,但是多态this
的作用类似于该类的隐藏类型参数,并且使用它也有类似的限制。例如,在g
内,obj['prop']
的类型又一次不知道是string
,这是因为对通用类型参数的说法存在局限性
function g<
T,
K extends keyof Pick<T, P<T, string>>
>(obj: T, prop: K): void { obj[prop].charAt(0) /*error*/}
尽管上面只是猜测(我会有点模糊),但解决上述错误的解决方案将解决this
的问题,即提出我们的约束,即只有string
键可以以不同的方式被传递。
function g3<
K extends string | number | symbol,
T extends Record<K, string>
>(obj: T, prop: K): void { obj[prop].charAt(0) /* ok*/ }
class C {
public alpha!: string;
public beta!: number;
public f(): void {
g3(this, "alpha"); // also ok as expected
g3(this, "beta"); //not ok
}
}
答案 1 :(得分:1)
很难不看编译器的源代码就很难分辨,但是有一点很清楚:this
内的C
没有类型C
,基于这样的观察,如果您添加类型断言this as C
:
class C {
public alpha: string;
public beta: number;
public f(): void {
g(this as C, "alpha"); // ok
错误消息中为this[keyof this]
提示的类型TObject[K]
暗示this
是"polymorphic this"类型-表示{{1 }}。
我不知道是什么导致C
在这里被视为多态的(也许总是假定它是多态的),但是不幸的是,它的键集不是一成不变的。
尽管可以证明,即使使用了多态,this
仍将是其键之一,并且"alpha"
类型将与this["alpha"]
兼容,如在{{1}中声明的那样},编译器不会遵循必要的逻辑,只是安全地运行-“如果不是从静态上知道它是不兼容的”。