You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: book/ru/chapters/034.(Типы) Обобщения (Generics)/content.md
+84Lines changed: 84 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -703,3 +703,87 @@ class Identifier<T> {
703
703
}
704
704
}
705
705
`````
706
+
707
+
## Модификаторы вариантности параметров типа in и out
708
+
709
+
Идентификация данных осуществляется при помощи типов и предназначена для предотвращения попадания в операции неподходящих для них значений. Каждая операция имеет свое низкоуровневое описание содержащее, в том числе и тип, к которому должно принадлежать пригодное для неё значение. Сверка типа значения с этим типом называется процессом выявления совместимости. Совместимость осуществляется по правилам вариантности, которые бывают четырех видов. Но прежде, чем рассмотреть каждый из них, ненадолго отвлечемся на _TypeScript_.
710
+
Поскольку _TypeScript_ реализует _структурную типизацию_, тип проще всего представить в виде обычного листка бумаги, который может содержать имена (идентификаторы) ассоциированные с какими-либо другими типами. Идентификатор + ассоциированный с ним тип = признак типа. Именно на основе этих признаков и осуществляется процесс выявления совместимости речь о которой пойдет сразу после того освещения ещё одной очень простой темы - иерархии наследования.
711
+
Представляя иерархию наследования в голове сразу вырисовывается картина из мира номинативной типизации, что неосознанно выбивает из колеи структурной. Поэтому сразу стоит сосредоточиться на интересующей нас детали - логическом обозначении иерархических отношений. Дело в том, что иерархия направлена сверху вниз, а значит более базовый тип расположен выше, чем его подтип. Таким образом, в логических выражениях представляющих иерархию базовые типы обозначаются, как большие (`>`) по отношению к своим подтипам. И наоборот. То есть, `SuperType > SubType` и `SubType < SuperType`. Но упомяну ещё раз, в структурной типизации нет понятия иерархия наследования, поскольку при сравнении берутся в расчет признаки типов, а не ссылки (`ref`). Но чтобы не забивать особо голову просто возьмем за правило, что тип, который обладает всеми признаками другого типа и кроме этого содержит дополнительные, будет считаться подтипом, а значит, в логических выражениях будет обозначаться меньшим (`<`).
712
+
713
+
`````ts
714
+
interfaceA {
715
+
f0:boolean;
716
+
}
717
+
interfaceB {
718
+
f0:boolean;
719
+
f1:number;
720
+
}
721
+
interface С {
722
+
f0:boolean;
723
+
f1:number;
724
+
}
725
+
726
+
// A > B или B < A
727
+
// A > C или C < A
728
+
// B = C или C = B
729
+
`````
730
+
731
+
Это очень просто и это знают все, но повторить все равно стоило, так как именно логические выражения нам помогут разобраться в количестве видов вариантов совместимости. Теперь, на основе полученной информации давайте рассмотрим варианты по которым может происходить проверка на совместимость.
732
+
733
+
И так, вариантов всего четыре и каждый из них имеет собственное название. Но чтобы было более понятно рассмотрим сценарий, когда переменной принадлежащей к типу `A` может быть присвоено значение с типом `B`.
734
+
735
+
`Ковариантность` предполагает, что проверка на совместимость завершится успехом в случаи, когда `B < A` или `B = A` (в номинативной типизации `B` подтип `A` ). `Контрвариантность` предполагает, что `B > A` или `B = A` (в номинативной бы это звучало, как базовый тип можно совместим с подтипом или самим собой, но не наоборот). `Инвариантность`, это когда совместимы исключительно при условии `B = A`. `Бивариантность` подразумевает `B < A`, `B > A` или `B = A`, то есть - все предыдущие варианты в одном.
736
+
737
+
А теперь к сути дела. В _TypeScript_ все типы проверяются на совместимость по ковариантным правилам, за исключением параметров функций, которые контрвариантны. Поскольку различные правила на сложных рекурсивных типах требуют дорогостоящие вычисления, _TypeScript_ реализует механизм явного аннотирования параметров типа при с помощью необязательных модификаторов `in` и `out`.
738
+
739
+
`in` указывает, что параметр типа ковариантен, а `out` контрвариантен. Но стоит сделать акцент на том, что с помощью этих модификаторов невозможно изменить правила по которым _TypeScript_ производит вычисления совместимости, а можно лишь их конкретизировать.
740
+
741
+
`````ts
742
+
typeSetter<T> = (param:T) =>void;
743
+
typeGetter<T> = () =>T;
744
+
745
+
/**
746
+
* Стандартный код.
747
+
* При сравнении двух сеттеров параметры в сигнатуре будут проверятся по контрвариантным правилам, а для геттеров возвращаемые типы по ковариантным.
748
+
*/
749
+
`````
750
+
`````ts
751
+
typeSetter<inT> = (param:T) =>void;
752
+
typeGetter<outT> = () =>T;
753
+
754
+
/**
755
+
* [Код с модификаторами]
756
+
* Правила будут идентичны предыдущему примеру. Разница лишь в явной конкретизации.
757
+
*/
758
+
`````
759
+
`````ts
760
+
typeSetter<outT> = (param:T) =>void; // [0]
761
+
typeGetter<inT> = () =>T; // [1]
762
+
763
+
/**
764
+
* [Код с модификаторами]
765
+
* К тому же, нельзя изменить поведение, то есть - нельзя поменять модификаторы местами!
766
+
*/
767
+
768
+
/**
769
+
* [0]
770
+
* Type 'Setter<sub-T>' is not assignable to type 'Setter<super-T>' as implied by variance annotation.
771
+
* Types of parameters 'param' and 'param' are incompatible.
772
+
* Type 'super-T' is not assignable to type 'sub-T'.ts(2636)
773
+
*/
774
+
775
+
/**
776
+
* [1]
777
+
* Type 'Getter<super-T>' is not assignable to type 'Getter<sub-T>' as implied by variance annotation.
778
+
* Type 'super-T' is not assignable to type 'sub-T'.ts(2636)
779
+
*/
780
+
`````
781
+
782
+
Проще всего воспринимать эти модификаторы, как указание на то, что тип будет использоваться во входных параметрах (`<in T>`) или выходных (`<out T>`) или и то и другое одновременно `<in out T>`.
783
+
784
+
`````ts
785
+
/**
786
+
* Указание на то, что тип используется во входных и в выходных параметрах.
0 commit comments