Почему TypeScript сужает тип для некоторых объявлений const, но не для других?

ОБНОВЛЕННЫЙ ВОПРОС (один основной вопрос)

Учитывая приведенный ниже фрагмент кода, почему тип сужается при объявлении константы как Version, но не для той, которая объявлена ​​как number?

type Version = 1 | 2 | 3;
const v: Version = 3;
if (v === 2) console.info('v equals 2'); // Fails compilation with "This comparison appears to be unintentional
                                        // because the types '3' and '2' have no overlap."
const n: number = 3;
if (n === 2) console.info('n equals 2'); // No compile error

ОРИГИНАЛЬНЫЙ ПОСТ

Учитывая фрагмент кода ниже:

  1. Почему первое сравнение (v > 2) проходит компиляцию, а последнее (v === 2) — нет?
  2. Почему ошибка компиляции для (v === 2) говорит ...types '3' and '2'..., а не ...types 'Version' and '2'?
type Version = 1 | 2 | 3;

const v: Version = 3;

if (v > 2) console.info('v is greater than 2');

if (v === 3) console.info('v equals 3');

if (v === 2) console.info('v equals 2'); // Fails compilation with "This comparison appears to be unintentional 
                                        // because the types '3' and '2' have no overlap."

Еще один чрезмерный вывод со стороны TypeScript. Очевидно, он игнорирует вашу Version аннотацию и на основании константности v делает вывод, что это тип 3, а не Version, только проверяя, что 3 можно назначить Version.

STerliakov 20.08.2024 16:49

@STerliakov, это скорее недоделанный пример. Отлично работает, если код был близок к реальному: Playground Link Но пример просто не настоящий, если указать постоянное значение.

VLAZ 20.08.2024 17:11

@jcalz Я ищу ответ о том, как компилятор TypeScript обрабатывает сравнения типов числовых литералов, и задаю два подвопроса, чтобы прояснить ситуацию. Лично я думаю, что вопрос был бы менее ясен, если бы я пропустил один из них.

Christian 20.08.2024 21:56

@Christian AFAIK компилятор в настоящее время не реализует необходимую арифметическую логику для сравнения типов в выражениях, которые используют операторы неравенства (например, >) — поэтому вы можете заменить любой тип числа в операции неравенства, и компилятор это разрешит. Однако арифметика не требуется компилятору для моделирования строгого равенства сравнений типов.

jsejcksn 20.08.2024 22:37
^ Я не понимаю, почему компилятор разрешает строгое сравнение двух литералов на равенство (потому что его можно свести к логическому литералу, а это означает, что условный оператор и связанный с ним оператор блока должны выполняться безоговорочно, если true или удалено, если false).
jsejcksn 20.08.2024 22:39

Поведение > и поведение === по сути независимы (последний не является числовым оператором как таковым), так что это действительно похоже на два разных вопроса в одном. Если вы хотите оставить их обоих здесь, это ваша прерогатива, но он предлагает ответы, в которых кто-то обращается к одному, но не к другому (ответ ниже, похоже, ничего не говорит вам о > и о том, почему его не волнует «перекрытие»). ). Если это будет отредактировано, чтобы задать один вопрос узкого круга, я вернусь к нему. В противном случае, удачи!

jcalz 20.08.2024 22:45

@jcalz, честно, я постараюсь сузить вопрос (без каламбура)

Christian 20.08.2024 22:48
^ Я имел в виду утверждение типа в этом комментарии (не константное утверждение).
jsejcksn 20.08.2024 22:50
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой 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 для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
10
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Когда TypeScript видит const v: Version = 3;, он игнорирует Version и определяет тип как тип числового литерала 3.

Если v есть 3:

  • (3 > 2) Никаких ошибок
  • (3 === 3) Никаких ошибок
  • (3 === 2) Ошибка «3» и «2» не пересекаются.

В следующем примере v — это параметр функции, который выводится как Version, а не 3. Все работает так, как и следовало ожидать, без ошибок.

type Version = 1 | 2 | 3;

function test(v: Version) {
  if (v > 2) console.info('v is greater than 2'); // No errors
  if (v === 3) console.info('v equals 3'); // No errors
  if (v === 2) console.info('v equals 2'); // No errors
}

test(3);
// Logs:
// "v is greater than 2" 
// "v equals 3" 

Обновление: О сужении

При использовании const v: Version = 3 TS сужается от Version до 3. Тогда анализ потока управления в каждом операторе if в некоторых случаях сужается до never.

const v: Version = 3;

if (v > 2) {
    type t = typeof v; // 3
}

if (v === 3) {
    type t = typeof v; // 3
}

if (v === 2) {
    type t = typeof v; // never
}

if (v !== 3) {
    type t = typeof v; // never
}

if (v !== 2) {
    type t = typeof v; // 3
}

При использовании const v: number = 3; TS НЕ сужается от number до 3. Тогда анализ потока управления не сможет сузиться number до более конкретного типа.

const v: number = 3;

if (v > 2) {
    type t = typeof v; // number
}

if (v === 3) {
    type t = typeof v; // number
}

if (v === 2) {
    type t = typeof v; // number
}

if (v !== 3) {
    type t = typeof v; // number
}

if (v !== 2) {
    type t = typeof v; // number
}

Нет. (3 !== 2) дает вам типы «3» и «2» без перекрытия (как и ожидалось), а (3 !== 3) работает без ошибок, как и (3 === 3). Если это правда или ложь, это не имеет никакого отношения к ошибке. Управляется перекрытием или неперекрытием.

Remo H. Jansen 20.08.2024 21:25

Вы правы, я могу привести к путанице. Я удалил его сейчас. Спасибо!

Remo H. Jansen 20.08.2024 21:28

Итак, тип «сужен» до буквального типа 3. Но, скажем, я бы объявил v как число, т. е. const v: number = 3;, тогда я не получаю никаких ошибок компиляции, так что теперь нет сужения?

Christian 20.08.2024 22:00

Сужение, вызывающее проблему, происходит в объявлении переменной. Я добавил более подробную информацию к ответу.

Remo H. Jansen 20.08.2024 22:45

спасибо за объяснение. Но тогда почему в одном случае оно сужается, а в другом нет?

Christian 21.08.2024 09:00

Именно так команда TypeScript решила это реализовать. Они рассматривают варианты использования, прежде чем реализовывать функцию, и в этом случае они, вероятно, определили, что когда люди используют типы числовых литералов вместо просто чисел, они, вероятно, действительно хотят сузиться до точного типа числовых литералов. Поэтому они сделали числовой литерал более агрессивным при сужении.

Remo H. Jansen 21.08.2024 10:49

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