推断通用 'this' 类型

Inferring generic 'this' type

question, I am now trying to create functions with an explicit this parameter 类型扩展接口 IModel 开始:

// interface for Model class

interface IModel {
    state: {}
}

// based on answer to this question 
// now functions will be bound to model instance, so need to specify 'this' parameter as Model

type SingleArgFunction<Model extends IModel, A> = (this: Model, x: A) => A;
type ArrayedReturnFunction<Model extends IModel, A> = (this: Model, x: A) => A[];

type SingleArgFunctionObject<Model extends IModel, AS extends object> = {
    [K in keyof AS]: SingleArgFunction<Model, AS[K]>
}

type ArrayedReturnFunctionObject<Model extends IModel, AS extends object> = {
    [K in keyof AS]: ArrayedReturnFunction<Model, AS[K]>
}

function makeArrayed<Model extends IModel, A>(f: SingleArgFunction<Model, A>): ArrayedReturnFunction<Model, A> {
    return function (x) {
        return [f.call(this, x)];
    }
}

function makeArrayedAll<Model extends IModel, AS extends object>(
    fs: SingleArgFunctionObject<Model, AS>
): ArrayedReturnFunctionObject<Model, AS> {
    const result = {} as ArrayedReturnFunctionObject<Model, AS>;

    (Object.keys(fs) as (keyof AS)[]).forEach(function<K extends keyof AS>(key: K) {
        result[key] = makeArrayed(fs[key]);
    })
    return result;
}

示例模型和类型定义:

interface MyModel extends IModel {
    state: {
        x: number;
    }
}

interface SingleArgFunctions {
    foo: SingleArgFunction<MyModel, number>
}

interface ArrayedReturnFunctions {
    foo: ArrayedReturnFunction<MyModel, number>;
}

直接创建ArrayedReturnFunctions对象即可,this被推断为类型MyModel:

const arrayedReturnFunctions1: ArrayedReturnFunctions = {
    foo(x) {
        return [x + this.state.x]; // ok
    }
}

或者,通过将 makeArrayed 应用于单个函数来创建对象也可以:

const arrayedReturnFunctions2: ArrayedReturnFunctions = {
    foo: makeArrayed(function (x) {
        return x + this.state.x; // ok
    })
}

但是,使用 makeArrayedAll 不起作用 - this 被推断为类型 IModel:

const arrayedReturnFunctions3: ArrayedReturnFunctions = makeArrayedAll({
    foo(x) {
        return x + this.state.x; // error - property x does not exist on type {}
    }
})

即使创建类型为 SingleArgFunctions 的对象然后将其传递给 makeArrayedAll 也不起作用:

const singleArgFunctions: SingleArgFunctions = {
    foo(x) {
        return this.state.x + x;
    }
}

const arrayedReturnFunctions4 = makeArrayedAll(singleArgFunctions); // error - IModel is not assignable to type MyModel 

为什么在使用 makeArrayedAllModel 类型没有被推断为 MyModel

Playground

对我来说,这看起来像是您希望分配给 makeArrayedAll() 输出的变量 arrayedReturnFunctions 的类型将为 [=12] 的输入提供足够的上下文类型=] 以便推断输入法的 this 上下文......但它没有发生。在这种情况下,上下文推理不会发生,我并不感到惊讶,但对于究竟为什么或如何强制它发生,我没有很好的答案。

现在我唯一的建议是注意类型推断在 "forward" 方向上会更好;也就是说,泛型函数的类型参数更容易从函数的 输入 推断出来,而不是从函数的 预期输出类型 推断出来。如果失败,您始终可以手动指定泛型类型参数。

这是一种手动指定模型类型参数并让编译器推断其余部分的方法,使用 currying:

const makeArrayedAllFor = <M extends IModel>() => <AS extends object>(
  fs: SingleArgFunctionObject<M, AS>) => makeArrayedAll(fs);

const arrayedReturnFunctions3: ArrayedReturnFunctions = makeArrayedAllFor<MyModel>()({
    foo(x) {
        return x + this.state.x;
    }
})

const arrayedReturnFunctions4 = makeArrayedAllFor<MyModel>()(singleArgFunctions);

虽然很麻烦,但现在可以用了。这是我目前能想到的最好的;也许有人有其他想法?哦,好吧,祝你好运!

playground link