Проблема с вашим примером кода состоит в том, что тип DependentMethod
зависит от типа MyModel
, который имеет свойства, типы которых определяются выводом DependentMethod
, который зависит от типа MyModel
, который ... ой. Тип является циклическим, так что компилятор не может это сделать. И ошибка внутри MyModel
говорит вам об этом:
usernameCapitalised = DependentMethod( // error!
//~~~~~~~~~~~~~~~~~~~ <-- 'usernameCapitalised' implicitly has type 'any'
//because it does not have a type annotation and is referenced directly or
//indirectly in its own initializer.
[ 'username' ],
function() {
return this.data.username.toUpperCase()
}
)
Это приводит к каскадному переносу большинства ваших свойств, и они также имеют типы any
. Тот факт, что экземпляры MyModel
ведут себя странно, неудивителен; вам в значительной степени необходимо исправить ошибки внутри MyModel
, прежде чем вы сможете ожидать, что компилятор сделает с ним разумные вещи. Таким образом, я считаю, что ошибка в первом методе связана с красной сельдью; это интересно, но я бы не стал это исправлять, изменив метод первым. Вместо этого я бы по возможности исправил основную округлость.
Теперь я не могу быть уверен, что пример кода в достаточной мере отражает ваш вариант использования, но предполагая, что это так: я вижу, что * Параметр 1015 *, передаваемый в DependentMethod
для каждого свойства MyModel
, зависит только от свойства data
MyModel
, а не от других свойств. Поэтому, возможно, DependentMethod
не должен ссылаться на MyModel<UnionOfList<Dependencies>>
, а вместо этого должен просто ссылаться на {data: Pick<MyData, UnionOfList<Dependencies>>
, например так:
declare const DependentMethod: <D extends keyof MyData, R>(
dependencies: D[],
fn: (this: { data: Pick<MyData, D> }) => R
) => (this: { data: Pick<MyData, D> }) => R;
Примечание: я не беспокоюсь о реализации DependentMethod
здесь ; Я изменил параметры типа generi c на более обычный (хотя и менее выразительный) символ в верхнем регистре; и я изменил параметр типа generi c, чтобы он представлял ключи, а не массив ключей. Теперь MyModel
не имеет ошибок:
class MyModel<K extends keyof MyData> {
data: Pick<MyData, K>
constructor(data: Pick<MyData, K>) {
this.data = data
}
usernameCapitalised = DependentMethod(
['username'],
function () {
return this.data.username.toUpperCase()
}
)
useridTimesTen = DependentMethod(
['userid'],
function () {
return this.data.userid
}
)
}
и ваши экземпляры MyModel
ведут себя так, как я предполагаю, что вы ожидаете:
const emptyModel = new MyModel({})
emptyModel.usernameCapitalised(); // error
emptyModel.useridTimesTen(); // error
const usernameModel = new MyModel({ username: "Alice" });
usernameModel.usernameCapitalised(); // okay
usernameModel.useridTimesTen(); // error
const useridModel = new MyModel({ userid: 1 });
useridModel.usernameCapitalised(); // error
useridModel.useridTimesTen(); // okay
const fullModel = new MyModel({ userid: 1, username: "Alice" });
fullModel.usernameCapitalised(); // okay
fullModel.useridTimesTen(); // okay
Если окажется, что DependentMethod()
требуется доступ к дополнительным свойствам MyModel
, тогда вам может потребоваться преобразовать его в базовый класс с такими свойствами в них и в расширяемый класс без них, и DependentMethod()
будет ссылаться только на базовый класс. Идея состоит в том, чтобы убедиться, что ваши типы «заземлены», а не циклически.
Хорошо, надеюсь, это поможет вам определить направление. Удачи!
Детская площадка Ссылка на код