调整作为函数参数传递的对象中方法的“ this”上下文

时间:2020-05-26 13:51:40

标签: typescript types casting typescript-generics

在将this上下文更新为作为参数传递的对象的方法时,我遇到了问题。

function decorate<T extends {
    [K in keyof T]: T[K] extends (this: infer This, ...args: infer Args) => infer Return
        ? (this: This & { abc: 10 }, ...args: Args) => Return
        : never;
}>(object: T) {
    // @ts-ignore: just a hack to show a simple example
    object.abc = 10;
    return object;
}

decorate({
   getString() { return "abc"; },
   doSomething() {
       const str: string = this.getString(); // Property 'getString' does not exist on type '{ abc: 10; }'.(2339)
       const abc: number = this.abc;
   }
});

TypeScript正确检测到abc,但是无法访问原始上下文。深入研究,似乎推断出Thisunknown

function decorate<T extends {
    [K in keyof T]: T[K] extends (this: infer This, ...args: infer Args) => infer Return
        ? (this: This, ...args: Args) => Return
        : never;
}>(object: T) {
    // @ts-ignore: just a hack to show a simple example
    object.abc = 10;
    return object;
}

decorate({
   getString() { return "abc"; },
   doSomething() {
       const str: string = this.getString(); // Object is of type 'unknown'. (2571)
       const abc: number = this.abc; // Object is of type 'unknown'. (2571)
   }
});

我试图直接将原始对象用作上下文,但是此时T仅被检测为具有未知属性的对象({}):

function decorate<T extends {
    [K in keyof T]: T[K] extends (...args: infer Args) => infer Return
        ? (this: T, ...args: Args) => Return
        : never;
}>(object: T) {
    // @ts-ignore: just a hack to show a simple example
    object.abc = 10;
    return object;
}

decorate({
   getString() { return "abc"; },
   doSomething() {
       const str: string = this.getString(); // Property 'getString' does not exist on type '{}'. (2339)
       const abc: number = this.abc; // Property 'abc' does not exist on type '{}'. (2339)
   }
});

是否有一种方法可以调整传递对象方法的上下文,或者有完全不同的方法来实现类似的效果?

1 个答案:

答案 0 :(得分:1)

我认为您最好的选择是使用ThisType。这是编译器的特殊标记接口,可让您轻松指定this的含义。将对象分配给键入为ThisType<T>的位置时,对于在该对象中定义的任何方法/函数,T被认为是this的类型。

有了这个,解决问题的方法变得非常简单:

function decorate<T>(object: T & ThisType<T & { abc: number }>) {
    return Object.assign( object, { abc:  10 });
}

decorate({
   getString() { return "abc"; },
   doSomething() {
       const str: string = this.getString(); // Property 'getString' does not exist on type '{}'. (2339)
       const abc: number = this.abc; // Property 'abc' does not exist on type '{}'. (2339)
   }
});

Playground Link