Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Модификаторы вариантности могут изменять правила совместимости типов #198

Open
bgenia opened this issue Mar 18, 2023 · 2 comments

Comments

@bgenia
Copy link

bgenia commented Mar 18, 2023

В разделе "Обобщения / Модификаторы вариантности параметров типа in и out" говорится:

in указывает, что параметр типа ковариантен, а out контрвариантен. Но стоит сделать акцент на том, что с помощью этих модификаторов невозможно изменить правила по которым TypeScript производит вычисления совместимости, а можно лишь их конкретизировать.

На самом же деле, модификаторы вариантности могут сужать вариантность, но не могут её расширять или менять на противоположную. То есть, если параметр типа автоматически выводится ковариантным, мы можем сделать его инвариантным, но не можем сделать бивариантным или контравариантным. Бивариантному параметру можно установить любую желаемую вариантность.

Пример:

// Здесь параметр T является бивариантным
type Example<T> = {}

declare let vsub: Example<1>
declare let vsuper: Example<1 | 2>

// Присваивание в обе стороны возможно
vsub = vsuper
vsuper = vsub
// Здесь параметр T является контравариантным
type Example<in T> = {}

declare let vsub: Example<1>
declare let vsuper: Example<1 | 2>

vsub = vsuper
vsuper = vsub // Ошибка

Если нужно, могу привести реальные примеры в которых это позволяет избежать ошибок.

@nauchikus
Copy link
Owner

@bgenia а это разве не баг? Сейчас вечер и я жутко устал, но мне кажется, что компилятор просто не берет в расчет не задействованный параметр. a | b не может быть совместимым с a поскольку может быть b, который не a.

@bgenia
Copy link
Author

bgenia commented Apr 11, 2023

@nauchikus Это не баг, это бивариантное поведение. Незадействовааный параметр считается бивариантным, так как его тип не влияет на структурную совместимость результирующих типов. Далее я показал пример что с помощью аннотаций можно сузить вариантность такого параметра.

Вот другой пример действия аннотаций:

// Как известно, параметры методов являются бивариантными даже при включенной strictFunctionTypes
// Такое поведение может приводить к ошибкам
type Example<T> = { f(arg: T): void }

declare let vsub: Example<1>
declare let vsuper: Example<1 | 2>

// Присваивание в обе стороны возможно
vsub = vsuper
vsuper = vsub
// Контравариантность можно задать искусственно с помощью аннотации in
type Example<in T> = { f(arg: T): void }

declare let vsub: Example<1>
declare let vsuper: Example<1 | 2>

vsub = vsuper
vsuper = vsub // ошибка

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants