Вышел TypeScript 5.6.
Релиз задержался на шесть дней, но это того стоило. Рассказываю, чего мы дождались.
Новые методы итераторов.
Мелочь, но как же я давно об этом мечтал. Теперь у итераторов есть методы map
, filter
и reduce
— прям как у массивов. Но в отличие от массивов, они позволяют работать с коллекциями лениво. Вот мой любимый фокус:
const numbers = [1, 2, 3];
const squares = Iterator
.from(numbers)
.map((x) => x ** 2);
numbers.push(4);
console.log(squares.toArray()); // => [1, 4, 9, 16]
Метод Iterator.from
— это ещё один новый метод. Он конвертирует переданный объект в объект итератора.
Мне нравится, что теперь можно оптимизировать цепочки вызовов map
и filter
. Например, вот одна из функций, которая используется при сборке этого сайта:
function getInnerText(target: HTMLElement): string {
return Iterator.from(target.childNodes)
.filter((node) => !(isHTMLElement(node) && node.classList.contains("code-listing-comment")))
.map((node) => node.textContent)
.toArray()
.join("");
}
Методы filter
и map
не создают промежуточные массивы (как в случае с методами типа Array
). Это может здорово сэкономить память, если у тебя куча элементов в массиве. Хотя странно, что не добавили метод join
, как у массивов.
Ну и, конечно, ленивость прекрасна тем, что позволяет работать с бесконечными структурами данных. Бесконечные массивы в работе никогда не приходилось использовать, но сама идея мне безумно нравится 🙃.
function* countFrom(start: number): Generator<number> {
for (let i = start; ; i++) {
yield i;
}
}
const firstFiveSquares = countFrom(0)
.map(x => x ** 2)
.take(5)
.toArray();
console.log(firstFiveSquares); // => [0, 1, 4, 9, 16]
Ошибиться в условиях теперь сложнее.
Такой код больше не скомпилируется:
// Компилятор выводит ошибку:
//
// This kind of expression is always truthy.
//
if (2) {
console.log("2 is truthy");
}
if
всегда истинно. Некоторые компиляторы могут убирать такие лишние проверки и оставлять только тело внутри if
. И правда, ведь если условие всегда true
, то и нет смысла его проверять. То же верно и для всегда ложных условий — код типа if (0) { ... }
можно спокойно удалять.
А более «умные» компиляторы — например, новый компилятор TypeScript — будут такой код не оптимизировать, а бить программиста по рукам. Иногда и правда важно обращать на это внимание человека. Когда я проверял тестовые задания на позицию фронтендера, я встретил примерно такой код:
if (typingAccuracy => thresholdAccuracy) {
console.log("passed");
}
Ну ты понял, да? Вместо <=
наш фронтендер сделал лямбда-выражение. А с новым компилятором шансы на трудоустройство были бы выше 😉.
Такие ошибки выдаются не только в if
. Если написать подобное в while
или в тернарном операторе, компиляция тоже не пройдёт.
Прощай, while (true)
? К счастью, нет. true
, false
, 1
и 0
воспринимаются как специальные значения и пропускаются компилятором. Их часто удобно использовать для отладки или тех же бесконечных циклов. Запрещать так писать — последовательно и безопасно, но жизнь это всем усложнило бы.
Похожие проверки теперь работают и для оператора ??
. Только там проверяется не на true
и false
, а на null
и не null
. Соответственно, и специальных значений там нет — 0 ?? 1
или false ?? true
тоже вызовет ошибку.
Импорт и экспорт бананов.
Майкрософт на примере бананов показали нам, что теперь можно импортировать и экспортировать даже невалидные идентификаторы — если заключить их в кавычки:
const banana = "🍌";
export { banana as "🍌" };
import { "🍌" as banana } from "./foo"
console.log(banana); // => 🍌
Это нужно для взаимодействия с другими языками. Например, с WebAssembly. Ведь каждый язык сам решает как называть переменные.
И о других изменениях.
О чём ещё стоит сказать:
- Новый флаг
--strictBuiltinIteratorReturn
делает итераторы строже и исправляет один из косяков проверки типов в TypeScript. - Новый флаг
--noUncheckedSideEffectImports
проверяет, что файл существует, когда пытаешься импортировать его. Даже если сам тайпскрипт не умеет работать с таким файлом (например, CSS модули). - И ещё один новый флаг
--noCheck
отключает проверку типов. И просто выдаёт JS 😐. Звучит странно, но это иногда полезно и позволяет иметь два параллельных процесса — один для проверки типов, а другой для генерации кода.
О других апдейтах ты всегда можешь почитать в официальных release notes .