Я предлагаю сделать ваши типы PageData
и Page
родовыми c в типах Page
объектов data
и methods
свойств, например:
interface PageData<D extends object, M extends Record<keyof M, () => any>> {
data: (this: Page<D, M>) => D;
methods: (this: Page<D, M>) => M;
main: (this: Page<D, M>) => void;
}
class Page<D extends object, M extends Record<keyof M, () => any>> {
name: string;
data: D;
methods: M;
constructor(name: string, data: PageData<D, M>) {
this.name = name;
this.data = data.data.call(this);
this.methods = data.methods.call(this);
data.main.call(this);
}
}
(Примечание: учитывая, что ваш пример PageData
явно использует свойства функции стрелки вместо методов внутри ее свойства methods
, я не буду беспокоиться о типе служебной программы ThisType<T>
, так как они не нужно беспокоиться о собственном this
контексте.)
Тогда возникает проблема с созданием PageData
объекта и устранением этих ошибок.
Я полагаю, вы не понимаете не хочу вручную аннотировать параметр this
всех его свойств и вместо этого хотел бы, чтобы компилятор выводил соответствующий тип this
. К сожалению, это, вероятно, будет невозможно сразу с одним объектом (см. соответствующий комментарий GitHub ); компилятор должен одновременно создавать и выводить тип, что не очень хорошо. Лучше быть способным делать вещи последовательно. Таким образом, если, как в вашем примере, свойство data
на самом деле ни от чего не зависит, свойство methods
зависит от data
, а свойство main
зависит от data
и methods
, тогда вы могли бы рассмотреть возможность создания строитель для PageData
объектов, который создает их последовательно:
const pageDataBuilder = {
data: <D extends object>(data: (this: Page<any, any>) => D) => ({
methods: <M extends Record<keyof M, () => any>>(methods: (this: Page<D, any>) => M) => ({
main: (main: (this: Page<D, M>) => void) => ({
data, methods, main
})
})
})
};
И вот как вы можете использовать его:
const data = pageDataBuilder.data(
function () {
return {
customName: this.name + "demo"
}
}).methods(function () {
return {
doSomething: () => this.data.customName.repeat(2)
}
}).main(function () {
console.log(this.data.customName);
console.log(this.methods.doSomething());
});
Это компилируется без ошибок, и затем это работает:
const page = new Page("demo", data);
page.data.customName; // string
Выглядит хорошо для меня. Надеюсь, это поможет; удачи!
Детская площадка ссылка на код