Как назначить типу значение по умолчанию (generic | type | class)?

TypeScript Есть некоторый класс.
Экземпляр класса содержит ссылки на тип производный от этого класса.

// Так я пытаюсь передать классу используемый тип, при этом если не передается
// параметр типа, он должен вывести тип `Some`(сам себя).
// Установить по умолчанию не вышло.
class Some<TItem extends Some<TItem> /* = Some // error: circular default */> {
  _item: TItem // должен быть либо `TItem extends Some` либо `Some`
  getSibling(): TItem {
    return this._item
  }
  getSelf(): this {
    return this
  }
}

const some = new Some()
some._item = some
// С этим все понятно - будет работать до любой глубины,
// но выведенный тип тоже не особо мне нравится - хотелось бы просто `Some`.
const s1 = some.getSelf().getSelf().getSelf()
//    -> Some<Some<unknown>>

// А это уже ломается
const s2 = some.getSibling().getSibling().getSibling()
//    ->       Some<unknown>   unknown        any


// Предположим, что этот класс, как и `Some`, может наследоваться, и так до бесконечности ...
// Т.е. и здесь мы должны как-то определить тип по умолчанию и передать его в базовый класс.
class SomeExtended<TItem extends SomeExtended<TItem>> extends Some<TItem> {
  _other: TItem
  getOther(): TItem {
    return this._other
  }
}

const se = new SomeExtended()
se._item = se
se._other = se

// SomeExtended<SomeExtended<unknown>>
const e1 = se.getSelf().getSelf().getSelf()

// Оба ниже не работают.
const e2 = se.getSibling().getSibling().getSibling()
const e3 = se.getOther().getOther().getOther()

Playground

Другими словами:

  • Класс умеет работать с типами, производными от самого класса(или с тем же интерфейсом).
  • Пользовательская функция может "попросить" ссылку на элемент(в примере getSibling()), и она должна получить реальный тип.

Хотелось бы иметь конструкцию, подобную этой:

class _Some<TItem extends Some<TItem> = Some> { /* ... */ }

Ответы (1 шт):

Автор решения: Alexander Lonberg

Интересное наблюдение:
Если создать фиктивный(вспомогательный) тип, который хоть и является круговой ссылкой, но через посредника, ошибок уже не возникает.

Решение простое до безобразия.

// Просто создаем вспомогательный тип `_Some = Some`
type _Some = Some
class Some<TItem extends Some<TItem> = _Some> {
  _item: TItem
  getSibling(): TItem {
    return this._item
  }
  getSelf(): this {
    return this
  }
}

const some = new Some()
some._item = some
const s2 = some.getSibling().getSibling().getSibling().getSibling()
//    ->         _Some         _Some        _Some        _Some
→ Ссылка