TypeScript: оболочка для реализации универсального класса

#typescript #generics #dry #typescript-generics

Вопрос:

Почему в машинописном тексте невозможно выполнить следующее?

 abstract class Generic<T> {
  // ...
}

class Specific1 extends Generic<string> {
  // ...
}

class Specific2 extends Generic<number> {
  // ...
}

// this is where stuff goes wrong, compiler wants me to provide generic type arguments
//                                     |
//                                     ˅
abstract class Wrapper<T extends Generic> {
  // ...
}

class Wrapper1 extends Wrapper<Specific1> {
  // ...
}


class Wrapper2 extends Wrapper<Specific2> {
  // ...
}
 

С Wrapper помощью строки универсального класса я пытаюсь выразить, что я хочу , чтобы классы входили только в Wrapper качестве универсального типа , который «специально реализован» Generic , т. Е. Specific1 и Specific2 , в данном случае.

В проекте, над которым я работаю, у меня больше общих типов Generic , чем показано здесь, а также больше реализаций, т. Е. Больше SpecificX классов. Я мог бы избежать этой проблемы, передав все общие типы тем, к WrapperX которым я переходил SpecificX раньше, вот так:

 class Specific1 extends Generic<type1, type2, type3> {
  ...
}

...

class Wrapper1 extends Wrapper<type1, type2, type3> {
  ...
}
 

… но я просто чувствую себя очень грязным, и мне интересно, есть ли лучший способ решить эту проблему. Я уже определил свой SpecificX класс и его универсальные типы и хочу избежать повторения этого в другом месте.

Просто СУХО, но я не могу понять, как это сделать в машинописном виде 🙁

Ответ №1:

Если Wrapper вы должны быть в состоянии обернуть что-либо Generic , и вас на самом деле не волнует, каким Specific может быть фактический тип, то явным образом пометьте параметр типа, как unknown это будет сделано. С помощью следующего вы можете создавать обертки для всего, что расширяется Generic .

 abstract class Wrapper<T extends Generic<unknown>>
 

Если Wrapper следует обернуть только один из Specific типов, то вы можете подойти к проблеме с типом объединения. Возможно, было бы неплохо создать новый тип, содержащий все параметры. Это, безусловно, более подробно, но предотвращает обертывание чего-либо произвольного (например Generic<ButNotASpecificType> ).

 class Specific1 extends Generic<string> {
  // ...
}

class Specific2 extends Generic<number> {
  // ...
}

type Specific = Specific1 | Specific2

abstract class Wrapper<T extends Specific>
 

Вот ссылка на игровую площадку для машинописи, демонстрирующая два подхода.

Комментарии:

1. Чувак, ты только что спас мне день. unknown оказалось именно то, что мне было нужно. У меня действительно есть некоторые ограничения, но я уже смоделировал их с помощью интерфейса. Спасибо!!!