Я пытаюсь написать определение типа для спецификации настраиваемого модуля, которую я здесь унаследовал. Не могу понять. Уловка состоит в том, что контекст это в вычисленном контексте в функции должен управляться из характеристики, так что shouldBeValueA управляется из keyA.
define.model("moduleName",
[
"jquery"
],
function($) {
return this.viewModel({
pub: {
properties: {
keyA: "valueA"
},
functions: {
keyB: this.computed(function() {
var shouldBeValueA = this.keyA;
})
}
}
})
})
Лучшее определение, которое у меня есть на данный момент:
interface Define {
model: (
name: string,
dependencies: string[],
moduleContext: <T>(this: {
computed: (context: (this: T) => any) => KnockoutComputed<any>,
viewModel: (options: {
pub: {
properties: T,
functions: any
},
}) => any;
},
...args) => void) => void;
}
declare var define: Define;
Но это ошибки: «Свойство keyA не существует для типа T»
Это непростой вопрос. Я думаю, что внутренний вызов this.viewModel() предотвращает вывод типа T «снизу вверх». Я немного поигрался с этим на игровой площадке и не смог заставить его работать. Либо T выводится как {}, либо кажется неразрешенным как T (как это делает ваш текущий код). Единственным обходным решением было определение model<T> вместо modelContext<T>, а затем вызов с использованием такого типа, как model<{ keyA: string }> ...
Размышляя об этом сегодня, мне могут понадобиться типы более высокого порядка в машинописном тексте, которые в настоящее время не поддерживаются. То есть, если я определяю тип для самой модели представления, я получаю type viewModel<T> = (options: { pub: { properties: T, functions?: any }, }) => any interface Define { model: ( name: string, dependencies: string[], moduleContext: <A, F extends viewModel<A>>(this: { computed: (context: (this: A) => any) => KnockoutComputed<any>, viewModel: F }, ...args) => any) => any; }






Я не уверен, что это поможет кому-то еще, но @kingdaro прав, что этот шаблон очень похож на API vue.js. В конце концов я построил определение типа, вдохновленное этим шаблоном.
interface RequireDefine {
model<TPubProps, TPubFuncs, TPrivProps, TPrivFuncs>(
name: string,
deps: string[],
factory: (
this: {
viewModel<TPubProps, TPubFuncs, TPrivProps, TPrivFuncs>(
options: ThisTypedViewModelOptions<TPubProps, TPubFuncs, TPrivProps, TPrivFuncs>
): TPubProps & TPubFuncs
): any
}
type ThisTypedViewModelOptions<TPubProps, TPubFuncs, TPrivProps, TPrivFuncs> =
object
& ViewModelOptions<TPubProps, TPubFuncs, TPrivProps, TPrivFuncs>
& ThisType<CombinedViewModelInstance<TPubProps, TPubFuncs, TPrivProps, TPrivFuncs>>
type CombinedViewModelInstance<TPubProps, TPubFuncs, TPrivProps, TPrivFuncs> = TPubProps & TPubFuncs & { priv: () => TPrivProps & TPrivFuncs }
type DefaultMethods = { [key: string]: (...args: any[]) => any };
interface ViewModelOptions<
TPubProps = object,
TPubFuncs = DefaultMethods,
TPrivProps = object,
TPrivFuncs = DefaultMethods
> {
ctor?: (options?: any) => void,
pub?: {
properties?: TPubProps,
functions?: TPubFuncs
},
priv?: {
properties?: TPrivProps,
functions?: TPrivFuncs
}
}
Это все еще не совсем идеально, но это был хороший опыт обучения адаптации типов vue к этой структуре модели представления.
Я бы сам не знал, как это сделать, но у Vue.js есть аналогичный API, и может быть полезно взглянуть на определения их типов для справки.