UPD
Хорошо, теперь я понимаю, что вы хотите, но есть еще несколько вопросов о том, как ваши объекты должны вести себя во время выполнения. Также у вас есть много круговой зависимости. С этим очень трудно иметь дело, и, честно говоря, я понятия не имею, что машинопись пытается сделать с твоим кодом.
Основная проблема, которая мешает мне сформулировать разумный ответ, - это не обобщение, но свойства объекта, которые производят неопределенное вложение. В основном это свойство target
на интерфейсе EventInterface
, каким оно должно быть? Это то же самое EventTargetInterface
, которое отправило событие? Это другой EventTargetInterface
? Как в обоих случаях инициализировать свойство target
? Так что я действительно смог заставить ваш пример (вроде?) Работать в TS песочнице , , но вы должны действительно подумать об изменении своего дизайна каким-то образом. С этими циклическими зависимостями действительно трудно иметь дело, и я не знаю, будет ли это работать, как ожидается, во всех случаях, эти генерики очень нестабильны. Это может вызвать у разработчиков больше головной боли, чем помощи.
Причина ошибки
Таким образом, основная причина заключается в конце ошибки, например:
'MyEventsMap' is assignable to the constraint of type 'M', but 'M' could
be instantiated with a different subtype of constraint 'MyEventsMap'.
Например, , возьмите следующую функцию
function fn<T extends string | number>(): T {
return "I don't work"
}
Это приведет к аналогичной ошибке
'"I don't work"' is assignable to the constraint of type 'T', but 'T'
could be instantiated with a different subtype of constraint 'string | number'.
Причина должна быть очевидна, T
не гарантированно включает тип string
, я мог бы также назовите это как fn<number>()
или fn<1>()
, и это не будет работать. В машинописи, которая причиняла мне много боли в шее, является тот факт, что вы не можете запретить вызов функции / создание экземпляра класса / использование псевдонима типа / сделать что-либо в отношении обобщений с подтипом противопоказания в extends
пункте . Это не совсем верно, есть хак, использующий вспомогательные типы, такие как
type EnsureMyMap<T> = T extends MyEventsMap<infer M> ? MyEventsMap<M> : never
Я отправил вопрос по этой теме c. Это не совсем подходит для этого случая, но это показывает идею. Вы можете проверить это и прочитать лучший ответ для конкретного примера .
Честно говоря, не знаю, как это относится к вашему конкретному примеру, и у меня нет wi sh, чтобы в него вникать. Может быть, это из-за значения по умолчанию, которое вы указываете для generi c в MyEvent
и MyEventTarget
, но я не уверен. Вы, вероятно, могли бы исправить все с помощью этого ключевого слова infer
и вспомогательных типов через час или два изо всех сил, но стоит ли это того?
Так что я бы предложил переосмыслить дизайн этих классов. В частности, было бы неплохо избавиться от свойства target
события, которое вызывает все эти циклические зависимости. Может тебе это не нужно? Может быть, вы могли бы передать его вместе с событием? Другим вариантом будет go менее строго соблюдать ограничения и уменьшить головную боль в будущем за счет худших подсказок (я имею в виду полное избавление от этой карты событий). Я могу обновить ответ еще раз, если вам нужна помощь по переработке всего этого, мне просто нужен пример времени выполнения, а не только объявление окружающего типа. Это все, что я могу предложить сейчас.
Оригинальный ответ
Ваш код кажется немного странным. Есть много циркулярных ссылок. Во-первых, я предполагаю, что во втором фрагменте кода тип должен иметь другое имя, иначе ItemsMap
, как предложено в комментариях Ини go, ссылается на себя:
type MyMap = {
first: Item<Categories.FIRST, ItemsMap>;
second: Item<Categories.SECOND, ItemsMap>;
}
Даже если это не тот случай, я думаю, что даже если код, который вы написали, сработал, он сделал бы то, что должен делать. Предположим, вы создали переменную
const map: MyMap = { first: /*...*/, second: /*...*/ }
Так что же должно быть map.first
? Объект типа
{
category: Categories.FIRST,
items: /*...*/
}
Так что же такое map.first.items
? ItemsMap
, например { [Category.FIRST]: /*...*/ }
. Так что же такое map.first.items[Category.FIRST]
? Объект типа
{
category: Category.FIRST,
items: /* ... */
}
Теперь go назад к предыдущему абзацу и еще один [Category.FIRST].items
к свойству, о котором я написал вопрос.
Так что это продолжается вечно. Он останавливается, если вы назначаете пустой объект для items
в какой-то момент, но в любом случае не похоже, что вам нужно бесконечно вложенное дерево элементов и категорий. Извините, если вы это сделаете, это просто не понятно из вашего кода и объяснений.
Так что единственное, что я могу предположить, это то, что вы не указали, что использовать класс Item
в определении ItemsMap
. В этом случае это будет иметь некоторый смысл для меня. Вы только что заявили
Цель состоит в том, чтобы передать "карту" перечисления / типа
Так что я думаю, что вы хотите сделать объект примерно таким (если Item
в ItemsMap
определение заменяется на { id: key }
, например):
const map = {
first: {
category: 'first',
items: {
first: { id: 'first' },
second: { id: 'second' },
},
},
second: {
category: 'second',
items: {
first: { id: 'first' },
second: { id: 'second' },
},
},
}
Даже здесь для меня не имеет особого смысла, почему вы хотите, чтобы ваши ключи были от тот же набор в map
объекте и во всех map[...].items
объектах. Опять же, извините, если вы не это имели в виду, я обновлю свой ответ, если вы предоставите более точное объяснение ваших целей. Также покажите пример объекта того типа, который вы хотите определить.