2016-11-13 2 views
2

Я пытаюсь сделать то, что, как я думаю, является «учебником» для полиморфного this, и он не работает, и я просто не могу понять это. Представьте себе, у меня есть некоторый abstract базового класса, который клонируемый, например:Полиморфно это в TypeScript

abstract class X { 
    abstract clone(): this; 
} 

Теперь я хочу, чтобы реализовать базовый класс, который обеспечивает clone реализацию:

class Y extends X { 
    constructor(private x: number) { super() } 
    clone(): this { return new Y(this.x + 1); } 
} 

Когда я делаю это, я получаю сообщение об ошибке что говорит Type Y is not assignable to type 'this'. Я совершенно смущен. Все, что я хочу передать здесь, это ограничение типа, которое, если подкласс X имеет вызванный метод clone, что тип вещи, который вы получите, будет идентичен подтипу. Разве это не то, что полиморфный this? Что я здесь делаю неправильно?

Here is a link к этому коду на игровой площадке TypeScript.

ответ

1

Нет, как сейчас, это не поддерживаемый случай использования для этого типа. Метод с возвращаемым типом должен возвращать экземпляр производного класса, даже если этот метод не переопределен в производном классе - см. code in this answer для примера, который оправдывает это.

Другими словами, учитывая abstract clone(): this декларацию, этот код должен быть действительным, даже если Z не отменяет clone себя, но вам реализацию clone в Y ломает его.

class Z extends Y { 
    f() { 
     let c: Z = this.clone(); 
    } 
} 

Так это выглядит как метод с this тип возвращаемого значения должен всегда return this.

ОБНОВЛЕНИЕ: Я нашел open issue с этим прецедентом на github, обозначенным как «принимающий запросы на тягу». Я не уверен, однако, если они действительно намерены его поддержать или планируют исправить cloneNode в своем компиляторе каким-то другим способом.

+0

Спасибо. Кажется, я понимаю, что вы имеете в виду. Но странно, что вы говорите, что это «не поддерживаемый случай использования», поскольку он явно упоминается (хотя и не реализован) в PR для этой функции в компиляторе https://github.com/Microsoft/TypeScript/pull/4910 –

+0

Ну, этот PR был объединен более года назад, и прошло 6 месяцев с тех пор, как проблема, по которой они якобы хотят исправить этот вариант использования, была отмечена как «принятие PR».Я добавил слова о том, что этот вариант использования в настоящее время не поддерживается. Если бы они действительно хотели его поддержать, это было бы исправлено давно. – artem

+0

Я представил альтернативный ответ, который включает в себя небезопасное обходное решение. –

1

Я отметил ответ артема как правильный, потому что он кажется правильным, что на самом деле это невозможно сделать на 100% безопасном пути.

Однако есть способ заставить компилятор обеспечить требуемое ограничение типа. Поэтому я решил включить этот ответ в случае, если он полезен людям. Единственным недостатком моего подхода является то, что он немного небезопасен по тем причинам, которые указывает артем, т. Е. Кто-то может расширить ваши классы, не предоставляя реализацию clone и создать ситуацию, когда возвращаемое значение на самом деле не будет тем, что вы утверждаете это.

Моим решением было просто добавить состав. Опять же, это вообще небезопасно. Но если вы никогда не выходите из классов, это прекрасно работает, насколько я могу судить. Так что мое решение было:

class Y extends X { 
    constructor(private x: number) { super() } 
    clone(): this { 
     return new Y(this.x + 1) as this; 
    } 
} 

Вы можете увидеть полную версию, которая компилирует here.

Смежные вопросы