Комментарии 11
Очень интересно! По сути игнорирование лишних полей это такая структурная типизация? Хотя и в С++ такое есть, производные классы (с лишними полями) можно использовать вместо базовых...
И еще, понятия "тип-сумма" и "тип-произведение" это ведь не совсем объединение и пересечение типов? Тип-сумма A+B может одновременно вместить объект только одного типа (A или B), а тип-объединение также может включать и пересечение типов (A&B), которое по сути является слиянием полей двух типов в один. Получается что тип-сумма это tagged union, а тип-объединение - tagged struct ?
Нет, тип-объединение - это вообще не tagged что-то там. Это буквально объединение множеств, только с типами.
Тип-произведение тоже никакого отношения к пересечению типов не имеет, тип-произведение это кортеж, ну или тот самый struct.
По сути игнорирование лишних полей это такая структурная типизация?
Удивительно, в языке со структурной типизацией вы нашли структурную типизацию...
тип-произведение это кортеж, ну или тот самый struct
Кстати, сам по себе кортеж в TS, например, [1|2, 3|4], не будет "полноценным" произведением, например, сужение типов (narrowing) не умеет с ним работать.
Пример. Если заменить для переменной х тип T на MT, то ошибок нет.
Есть даже тайп-челлендж на эту тему, с произвольной рекурсивной дистрибуцией.
Хороший вопрос! Типизация в TS структурная. Сами объединения в TS - untagged, хотя можно уточнить принадлежность к типу. А вообще провести параллели между типом-суммой и типом-произведением с объединением и пересечением в TS - неплохая тема для отдельного поста.
Добавлю ещё от себя.
1) При пересечении объектов, если есть совпадающие ключи, то значения в этих ключах пересекаются: {a: A, b: B1} & {c: C, b: B2} = {a: A, b: B1 & B2, c: C}
2) Пересечение функций - то же самое, что их перегрузка.
3) Объединение функций можно рассматривать как одну функцию, аргументы которой являются пересечениями:
type F = ((a: 1 | 2, b: 5 | 6) => void) | ((a: 2 | 3) => void);
declare const f: F; // возможные вызовы: f(2, 5) и f(2, 6)
Здесь первый параметр стал пересечением, а второй нет, но он обязателен. Суть: мы не знаем, какая именно функция из двух возможных в переменной f, но пересечения подходят для обеих.
При пересечении объектов, если есть совпадающие ключи, то значения в этих ключах пересекаются
Только пересекаются всё-таки типы, а не объекты.
Объединение функций можно рассматривать как одну функцию, аргументы которой являются пересечениями
В контравариантной позиции объединения выводится пересечение, это не только с функциями работает.
type X<in T> = {}
type t1 = X<1 | 2> | X<2 | 3> extends X<infer R> ? R : never // 2
Спасибо, это интересно. Один момент, второй пример как будто не удачный, там же нет ничего неожиданного - тип FnObjArg требует чтобы аргумент содержал число n:
type FnObjArg = { n: number };
const arg2 = {m: 2} as const;
const res = fn(arg);
В оригинальном примере в доках тип объявляет все члены как необязательные, но при передаче аргумента с какими-то другими полями возникает ошибка.
TypeScript: операции Union и Intersection в свете теории множеств