首页 > 解决方案 > 为什么在推断“this”的类型与显式接收它时,这种映射/条件类型的行为会有所不同?

问题描述

考虑以下代码,它使用v2.8 中引入的TypeScript 语言功能(条件类型):

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是它选择满足属性类型扩展的约束的属性名称。函数然后在类型参数约束中使用类型,例如:TObjectTPropertySuperTypegg2P

在这里,因为C.alphais of typestringC.betais of type number,我希望g/ g2with的所有五次调用prop === "alpha"都能编译,而所有五次 with 的调用都prop === "beta" 不能编译。

但是,调用g(this, "alpha")不会编译,如果您将此代码粘贴到TypeScript playground中您会看到。错误是:

Argument of type '"alpha"' is not assignable to parameter of type 'this[keyof this] extends string ? keyof this : never'.

为什么这个特定的调用会失败?我猜这与 TypeScript 如何推断 的类型有关this,但细节对我来说是模糊的。

标签: typescripttypescript2.8

解决方案


我同意 arthem 最可能的罪魁祸首是 polymorphic this。虽然很明显的类型this将是多态的this。虽然您可以肯定地说C['alpha']is of type stringthis['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
    }
}

推荐阅读