Inferring generi c 'this' type - PullRequest
       62

Inferring generi c 'this' type

0 голосов
/ 26 января 2020

Исходя из этого вопроса, я сейчас пытаюсь создать функции с явным этим параметром типа расширяющего интерфейс IModel:

// interface for Model class

interface IModel {
    state: {}
}

// based on answer to this question https://stackoverflow.com/q/59895071/374328
// 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 

Почему тип Model не выводится как MyModel, когда Использование makeArrayedAll?

Детская площадка

1 Ответ

1 голос
/ 26 января 2020

Для меня это выглядит так, как будто вы надеетесь, что тип переменной arrayedReturnFunctions, для которой вы назначаете вывод makeArrayedAll(), обеспечит достаточно контекстной типизации для ввода makeArrayedAll(), так что методы ввода ' this контекст будет выведен ... но этого не происходит. Я не совсем удивлен, что контекстный вывод не произойдет в этом сценарии, но у меня нет точного ответа о том, почему и как заставить это произойти.

Прямо сейчас мое единственное предложение состоит в том, чтобы отметить, что вывод типа имеет тенденцию работать лучше в направлении "вперед"; то есть параметры типа обобщенной функции c легче выводить из входа этой функции, а не из ожидаемого типа вывода функции. И если это не удается, вы всегда можете вручную указать параметр типа generi c.

Вот один из способов вручную указать параметр типа модели и позволить компилятору определить остальное, используя curry :

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);

Теперь это работает, хотя и громоздко. Это лучшее, что я могу себе представить на данный момент; может у кого-то есть другие идеи? Ну что ж, удачи!

ссылка на игровую площадку

...