Ошибка сужения типа TypeScript с forEach - PullRequest
2 голосов
/ 03 августа 2020

Помогите, пожалуйста, разобраться, насколько узкие типы TS. У меня есть простая функция foo, которая имеет forEach итератор по произвольному массиву. Понятно, что console.log запишет false после выполнения этого кода, но TS настаивает, что это true, что неверно.

Я ожидаю, что TS не сможет обрабатывать forEach или аналогичные функции из-за возможной асинхронности он должен предложить boolean. Эта ситуация действительно расстраивает меня и снижает мою продуктивность, потому что мне нужно перепроверить все 10 раз, чтобы доказать, что TS неправильный, а мой код правильный.

function foo() {
  let canActivate = true;

  ['foo'].forEach(() => {
    canActivate = false;
  })

  console.log(canActivate);
}

foo();

детская площадка

Ответы [ 2 ]

2 голосов
/ 03 августа 2020

В этом отличном сообщении в блоге объясняется, что это функция, а не ошибка. Я процитирую здесь большой кусок (курсив добавлен):

Пример

let a: number | null = 42

makeSideEffect()

a // is `a` still a number?

function makeSideEffect() {   
  // omitted... 
} 

...

Можно спросить [the] компилятор, чтобы определить, что делает makeSideEffect, поскольку мы можем предоставить источник функции. Однако это практически невозможно из-за внешней функции и (возможно, полиморфной c) рекурсии. Компилятор попадет в бесконечные циклы, если мы дадим ему указание вывести произвольные глубокие функции, как проблему остановки как таковую. последовательная стратегия. Естественно, у нас есть две альтернативы:

  • Предположим, что каждая функция не имеет соответствующего побочного эффекта : например, присваивание типа a = null. Мы называем это optimisti c.
  • Предположим, каждая функция имеет побочный эффект. Мы называем эту стратегию pessimisti c.

Spoiler: TypeScript использует стратегию optimisti c.

Еще один короткий отрывок:

Никакое ключевое слово не скажет компилятору [], будет ли функция обратного вызова вызываться немедленно , ни анализ c не скажет поведение функция: setTimeout и forEach одинаковы с точки зрения компилятора.

Таким образом, следующий пример не будет компилироваться.

var a: string | number = 42 // smart cast to number
someArray.forEach(() => {
  a.toFixed() // error, string | number does not have method `toFixed`
})

Вот и все. В сообщении также объясняется, что нет решения , чтобы заставить TypeScript распознавать побочные эффекты функции forEach или любой другой функции, кроме немедленно вызываемых функций. Это означает, что вы можете:

  • Использовать обычный для l oop вместо forEach, что приводит к правильному выводу окончательного canActivate как boolean а не true.
  • По возможности используйте немедленно вызываемые функции. Или ...
  • Придерживайтесь парадигм функционального программирования (FP) при использовании функций в стиле FP таких как map, filter, forEach и т.д. c. Это означает стремление к неизменности, отсутствие побочных эффектов и т. Д.

В общем, плохие новости. Надеюсь, что в будущем TypeScript получит какую-то функцию, которая позволит нам предупреждать компилятор, когда функция изменяет определенную переменную - по крайней мере, чтобы предотвратить преждевременное предположение, что то, что изначально было истинным, всегда останется таковым.

0 голосов
/ 03 августа 2020

Кажется, что вы прервали вывод типа где-то посередине из-за console.log побочного эффекта) Пожалуйста, посмотрите здесь строку 11

Кроме того, TS не выводит типы сбоку - такие эффекты, как forEach. Это задумано. Существует множество функций TS, которые помогают TS определять типы. Посмотрите эту статью https://www.typescriptlang.org/docs/handbook/advanced-types.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...