Почему функции Contravariance с их параметрами в машинописном тексте?

Недавно я изучал общие концепции Typescript. У меня проблема с пониманием: «Почему функции контравариантны со своими параметрами?». Я знаю это:

  1. Covariance - это ifT extends U (T присваивается U), верно, что G<T> extends G<U> (G<T> также присваивается G<U>)

  2. Contravariance - это если T extends U, то обязательно придет вывод, что G<U> extends G<T> (теперь G<U> можно присвоить G<T>).

Прежде чем задать вопрос на этом форуме, я прочитал следующие сообщения. Однако мне все еще трудно понять, почему функции в Typescript являются контравариантными по своим параметрам:

Я вижу, что не только функции в машинописном тексте контравариантны своим параметрам, но и некоторые другие языки также верны.

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

«не только […] в машинописном тексте, но и в некоторых других языках […] также» — я бы удивился, если бы существовал какой-нибудь язык, в котором конструктор типа Function не является контравариантным в типах параметров?

Bergi 02.09.2024 10:51

Я думаю ваша вторая ссылка прекрасно объясняет это на примере того, как работают подтипы функций

Bergi 02.09.2024 10:54

@ Берги, я читал это несколько раз и не понимаю, почему Dog -> Dog является супертипом Greyhound → Greyhound с предположением: Greyhound ≼ Dog ≼ Animal.

LeoPkm2-1 02.09.2024 11:45

Это не так. В статье на «вопрос 1» ответ «нет».

Bergi 02.09.2024 12:31

прочитав вторую ссылку, я не понимаю, почему Greyhound → Greyhound не является подтипом Dog -> Dog, там не было четкого объяснения или примера, поэтому я не понимаю, хотя я прочитал это много времени и потратил много времени на ее обдумывание.

LeoPkm2-1 02.09.2024 14:42

Предположим, у вас есть переменная let f: Dog → Dog, поэтому вы можете присвоить этой переменной любое значение подтипа Dog → Dog. Теперь вы называете f(dog) (где dog — это Dog). Можете ли вы назначить функцию типа Greyhound → Greyhound функции f, иначе вызов завершится неудачно? Вы не можете, так как это означало бы передачу Dog функции, которая принимает только Greyhound, поэтому Greyhound → Greyhound не является подтипом Dog → Dog.

Bergi 02.09.2024 19:24

(Вместо переменной, которой вы присваиваете функцию, в статье используется функция f, которая принимает такую ​​функцию в качестве аргумента - то же самое)

Bergi 02.09.2024 19:25

@Bergi, я не понимаю твоего утверждения: «поскольку это означало бы передачу Dog функции, которая принимает только Greyhound». Я не знаю Dog, на который вы ссылаетесь, это Dog тип или dog переменная, потому что вы сказали: передача Dog в функцию, которая принимает только Greyhound

LeoPkm2-1 02.09.2024 19:54

@Bergi, если Greyhound расширить Dog , мы можем передать переменную dog в функцию require param, имеющую тип Greyhound (Greyhound ->Greyhound)

LeoPkm2-1 02.09.2024 19:57

Нет, ты не можешь. Если Greyhound имеет дополнительное свойство по сравнению с Dog (все еще extends Dog), вы не можете использовать Dog, у которого нет этого свойства, в месте, где ожидается Greyhound. (Принимая во внимание, что вы можете использовать экземпляр Greyhound в любом месте, где ожидается Dog).

Bergi 02.09.2024 20:12

@ Берги, я неправильно понимаю значение подтипа. после рассмотрения вашего комментария выше, могу ли я заявить, что «подтип — это тип, который заменяет позицию, требующую родительского типа». Когда я присваиваю переменную Greyhound переменной Dog, переменная принимается, потому что Greyhound является Dog, другими словами, Greyhound может играть роль Dog (Greyhound имеет все свойства, которые Dog требуются). Таким образом, он может стоять в положениях, требующих типа Dog.

LeoPkm2-1 02.09.2024 20:39

Да, именно это

Bergi 02.09.2024 20:54

@Bergi, когда функция имеет тип Greyhound → Greyhound, присвоенный Dog → Dog. он не принимается, поскольку целевая функция (Dog → Dog) (левая часть присваивания) принимает экземпляры Dog в качестве аргумента. Однако исходная функция Greyhound → Greyhound (правая часть присваивания) не принимает переменную Dog в качестве аргумента. Таким образом, Greyhound → Greyhound не может играть роль Dog → Dog (не заменять Dog → Dog). Следовательно, Greyhound → Greyhound не является подтипом Dog → Dog.

LeoPkm2-1 02.09.2024 20:56

Да, вот и все!

Bergi 02.09.2024 20:58

@Bergi, тип возвращаемого значения меняется на тип функции params. Поскольку subtype является supertype, подтип содержит все члены, которые требуются супертипу. Поэтому тип возвращаемого значения должен быть либо Greyhound, либо другой подтип Dog, либо даже Dog.

LeoPkm2-1 02.09.2024 21:07

Да, возвращаемый тип Function является ковариантным.

Bergi 02.09.2024 21:13

Спасибо, с вашими объяснениями все понятно, спасибо большое

LeoPkm2-1 02.09.2024 21:18
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
4
17
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я буду использовать конкретный пример Animal и Dog.

class Animal {
  move(distanceInMeters: number = 0) {
    console.info(`Animal moved ${distanceInMeters}m.`);
  }
}
 
class Dog extends Animal {
  bark() {
    console.info("Woof! Woof!");
  }
}

Есть два варианта разрешения присваивания между Dog и Animal: разрешено ли нам заменять Dog на Animal и можем ли мы заменить Animal на Dog.

const animal: Animal = new Dog; 
animal.move(10);

Потому что extends означает, что у нас есть участники базы, это нормально. Typescript позволяет это

const dog: Dog = new Animal;
dog.bark();

В bark нет метода Animal, поэтому во время выполнения это не удастся. Машинопись это запрещает.

Теперь давайте посмотрим на типы функций.

const dog: Dog = new Dog;
const animal: Animal = new Animal;

type UseAnimal: (animal: Animal) => void;
type UseDog: (dog: dog) => void;

const useAnimal: UseAnimal = (animal: Animal) => { animal.move(10); }
const useDog: UseDog = (dog: Dog) => { dog.bark() }

Разработчики языка имеют такой же выбор параметров функций. Давайте посмотрим, что произойдет, если мы разрешим два случая

const dogFn: UseDog = useAnimal;
dogFn(dog);

Поскольку мы передаем Dog в useAnimal, все в порядке, оно может move(10), как указано выше. Машинопись позволяет это.

const animalFn: UseAnimal = useDog;
animalFn(animal)

Animal, к которому мы переходим, useDog вызовет сбой во время выполнения, потому что в нем отсутствует bark(). Машинопись это запрещает.

прочитав ваш ответ, я частично понял. Однако у меня все еще есть некоторые неясные моменты, можете ли вы помочь мне их прояснить?

LeoPkm2-1 02.09.2024 12:45

когда я запускаю const animalFn: UseAnimal = useDog;, компилятор выдает мне ошибку: Type 'UseDog' is not assignable to type 'UseAnimal'. Types of parameters 'd' and 'a' are incompatible. Property 'bark' is missing in type 'Animal' but required in type 'Dog'

LeoPkm2-1 02.09.2024 12:56

@LeoPkm2-1 Да, это пример того, что не сработает. const dog: Dog = new Animal; тоже ошибка

Caleth 02.09.2024 13:05

Это причина, по которой выдается ошибка T при реализации функции, которая имеет определенный тип, например: animalFn: UseAnimal, если доступ параметров к свойствам или методам, которые не существуют (или не описаны) в типе параметров, является ошибкой. Хотя Dog можно присвоить Animal (собака может перейти в параметр Animal), ожидаемый тип (форма) параметра в функции (animalFn) равен Animal?

LeoPkm2-1 02.09.2024 13:38

Да, вы не можете передать произвольный Animal в useDog, но вы можете передать произвольный Dog в useAnimal

Caleth 02.09.2024 13:40

Когда машинописный скрипт видит, что реализация animalFn (это useDog) имеет параметр Dog, имеющий метод, который не существует в ожидаемом типе параметра (Animal). Он сразу же выдает ошибку, чтобы предотвратить ошибку, как я сказал в комментарии выше?

LeoPkm2-1 02.09.2024 13:43

Да, попытка назначить это является ошибкой, потому что в противном случае поведение во время выполнения было бы плохим.

Caleth 02.09.2024 13:45

,разумно ли мое объяснение того, почему const animalFn: UseAnimal = useDog; предотвращается в Ts?

LeoPkm2-1 02.09.2024 13:48

или мое объяснение неверно или чего-то не хватает? Пожалуйста, скажите мне, что это такое.

LeoPkm2-1 02.09.2024 13:51

@LeoPkm2-1 Да. но это только половина того, что означает контравариантность. Ts пришлось реализовать проверку, которая принимает const dogFn: UseDog = useAnimal;, типы не совсем совпадают.

Caleth 02.09.2024 13:51

Рассмотрев ваш интересный пример, у меня возникли некоторые предположения о том, почему Ц принимает const dogFn: UseDog = useAnimal; Не могли бы вы помочь мне проверить их правильность или неправильность?

LeoPkm2-1 02.09.2024 13:58

Давайте продолжим обсуждение в чате.

Caleth 02.09.2024 14:02

Другие вопросы по теме

Похожие вопросы

Получите доступ к этому в функции, которая является свойством объекта
Создание сопоставлений на основе перечислений, включая обеспечение ошибок компилятора
Почему сопоставление массива не работает с той же записью сопоставления рекламы?
React Router обнаружил следующую ошибку во время рендеринга. Ошибка: отрисовано меньше перехватчиков, чем ожидалось
Почему машинописный текст не позволяет частично указывать аргументы типа, чтобы создать новую универсальную функцию из другой универсальной функции?
Компонент React со свойством «as», которое указывает на другой компонент и также наследует его свойства
Введите параметры из параметров функции
Как ввести динамический компонент в общий компонент Vue?
Можно ли заставить все дополнительные свойства интерфейса машинописного текста принимать значение null?
Возможно ли в TypeScript отключить правило TS (1345)