Настройте контекст методов this в объекте, переданном в качестве аргумента функции - PullRequest
0 голосов
/ 26 мая 2020

У меня проблема с обновлением контекста 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 правильно, но потерял доступ к исходному контексту. Если копнуть глубже, выясняется, что предполагаемый This равен unknown:

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 Ответ

1 голос
/ 26 мая 2020

Думаю, лучше всего использовать 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...