Вышел TypeScript 5.5.
вышла новая версия TypeScript. В ней куча небольших улучшений и даже сломана обратная совместимость. Я его уже попробовал, так что давай посмотрим что же там такое.
Предикаты типов теперь выводятся.
Предикаты типов — это когда мы пишем, что функция возвращает argument is Type
, а TypeScript использует это, чтобы уточнять типы в if
. Вот так:
function isHTMLElement(value: EventTarget | null): value is HTMLElement {
return value instanceof HTMLElement;
}
const button = document.createElement("button");
button.addEventListener("click", ({currentTarget}) => {
if (isHTMLElement(currentTarget)) {
// Здесь currentTarget имеет тип HTMLElement! Ура!
}
});
Но убрать возвращаемый тип функции я не мог — вывод типов внутри if
просто не сработал бы. Потому что TypeScript до версии 5.5 выводил возвращаемый тип как boolean
.
А теперь могу:
function isHTMLElement(value: EventTarget | null) {
return value instanceof HTMLElement;
}
Улучшение маленькое, но мне очень его хотелось. Потому что вот так я сделать не мог:
const items = ["строка", 4, "тоже строка", 7];
const uppercasedItems = items
.filter((item) => typeof item === "string")
.map((item) => item.toUpperCase());
/* При попытке вызвать метод toUpperCase возникает ошибка:
*
* Property 'toUpperCase' does not exist on type 'string | number'.
* Property 'toUpperCase' does not exist on type 'number'.
*/
filter
не работает в TypeScript до версии 5.5. Пример надуманный, но я точно помню, что мне это нужно было на работе.
Код не работает потому что анонимные функции выводят свои типы так же, как и обычные. И в такой ситуации им тоже нужно было указывать возвращаемый тип явно. А писать возвращаемый тип для анонимной функции — некрасиво. Но теперь всё круто и filter
возвращает массив с правильными типами:
const items = ["строка", 4, "тоже строка", 7];
const uppercasedItems = items
.filter((item) => typeof item === "string")
.map((item) => item.toUpperCase());
console.log(uppercasedItems); // => ["Строка", "Тоже строка"]
filter
работает в TypeScript 5.5. Но что-то может сломаться. Теперь типы выводятся точнее и старый код может не работать. Вот пример:
// Раньше тип nums выводился как (number | null)[], а теперь это number[]
const nums = [1, 2, 3, null, 5].filter(x => x !== null);
nums.push(null); // Ошибка!
Если такое случилось, просто укажи явный тип и всё будет хорошо.
Уточнение типов теперь работает и с индексацией.
Так не работало:
function getUppercasedValue(obj: Record<string, unknown>, key: string): string | null {
if (typeof obj[key] === "string") {
return obj[key].toUpperCase();
}
return null;
}
/* Тип obj[key] не уточняется и возникает ошибка:
*
* Object is of type 'unknown'.
*/
А теперь компилятор понимает, что ни obj
, ни key
не меняются и правильно выводит тип выражения:
function getUppercasedValue(obj: Record<string, unknown>, key: string): string | null {
if (typeof obj[key] === "string") {
return obj[key].toUpperCase();
}
return null;
}
const person = {
name: "Denis",
age: 24,
};
console.log(getUppercasedValue(person, "name")); // => DENIS
Регулярные выражения теперь проверяются на этапе компиляции.
Потрясающая фича. Я правда в восторге и даже понял зачем нужны литералы регулярок в языках программирования.
// Компилятор проверил синтаксис и сказал, что нашёл непонятную скобку:
// Unexpected ')'. Did you mean to escape it with backslash?
/(\d+))/
// А это уже не просто проверка синтаксиса — компилятор понял, что я
// сделал ссылку на вторую группу, хотя второй группы в регулярке нет!
// This backreference refers to a group that does not exist.
// There are only 1 capturing groups in this regular expression.
/(\d+)\2/
// И флаги он проверяет. Говорит, используешь наборы символов
// из Юникода, а флагов Юникода не поставил:
// Unicode property value expressions are only available when the
// Unicode (u) flag or the Unicode Sets (v) flag is set.
/\p{L}/
// А ещё предупреждает, если какая-то фича регулярок недоступна:
// Named capturing groups are only available
// when targeting 'ES2018' or later.
/(?<digits>\d+)\k<digits>/
Теперь можно использовать методы множеств.
Теория множеств — это круто, а типизированные операции над множествами — ещё лучше.
const imperativeLanguages = new Set(["C", "JS"]);
const functionalLanguages = new Set(["Haskell", "JS"]);
console.log(
imperativeLanguages.union(functionalLanguages),
imperativeLanguages.intersection(functionalLanguages),
); // => Set (3) {"C", "JS", "Haskell"}, Set (1) {"JS"}
И много чего ещё.
Про остальное скажу кратко потому что это в основном про конфигурации и декларации там всякие, а это скучно.
- Теперь можно писать
import
в JSDoc. Это чтобы импортировать типы в JavaScript, не импортируя модуль (import type
ведь только в TypeScript есть). - Добавили флаг
--isolatedDeclarations
чтобы быстрее генерировать файлы деклараций. - Добавили переменную
${configDir}
вtsconfig.json
иjsconfig.json
. - Больше нельзя создать переменную с именем
undefined
(да-да, а можно было).
А компилятор стал быстрее и сильно похудел (что для компиляторов хорошо). Если хочется больше подробностей о релизе — вот ссылка на release notes.